From cecd5733a8e455677f61b1eb861133f50954e8a5 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 17 Oct 2018 20:38:13 +0200 Subject: [PATCH 001/118] Basic file for getting the fix working --- pkg/commands/git.go | 12 ++++++- pkg/commands/os.go | 88 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 0e974b567..b9f4bee91 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -339,7 +339,17 @@ func (c *GitCommand) Push(branchName string, force bool) error { forceFlag = "--force-with-lease " } - return c.OSCommand.RunCommand(fmt.Sprintf("git push %s -u origin %s", forceFlag, branchName)) + cmd := fmt.Sprintf("git push %s -u origin %s", forceFlag, branchName) + return c.OSCommand.DetectUnamePass(cmd, func(passOrUname string) string { + if passOrUname == "password" { + return "some password" + // ask for password + } + if passOrUname == "username" { + // ask for username + } + return "" + }) } // SquashPreviousTwoCommits squashes a commit down to the one below it diff --git a/pkg/commands/os.go b/pkg/commands/os.go index c8ca40f29..5e11844b6 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -1,13 +1,17 @@ package commands import ( + "bufio" "errors" "os" "os/exec" + "regexp" "strings" + "github.com/ionrock/procs" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/kr/pty" "github.com/mgutz/str" @@ -57,6 +61,90 @@ func (c *OSCommand) RunCommandWithOutput(command string) (string, error) { ) } +// RunCommandWithOutputLive runs a command and return every word that gets written in stdout +// Output is a function that executes by every word that gets read by bufio +// As return of output you need to give a string that will be written to stdin +// NOTE: If the return data is empty it won't written anything to stdin +// NOTE: You don't have to include a enter in the return data this function will do that for you +func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) error { + splitCmd := str.ToArgv(command) + cmd := exec.Command(splitCmd[0], splitCmd[1:]...) + + cmd.Env = procs.Env(map[string]string{ + "LANG": "en_US.utf8", + "LC_ALL": "en_US.UTF-8", + }, true) + + tty, err := pty.Start(cmd) + + if err != nil { + return err + } + + defer func() { _ = tty.Close() }() + + go func() { + // Regex to cleanup the command output + re := regexp.MustCompile(`(^\s*)|(\s*$)`) + + scanner := bufio.NewScanner(tty) + scanner.Split(bufio.ScanWords) + for scanner.Scan() { + toWrite := output(re.ReplaceAllString(scanner.Text(), "")) + if len(toWrite) > 0 { + tty.Write([]byte(toWrite + "\n")) + } + } + }() + + if err := cmd.Wait(); err != nil { + return err + } + + return nil +} + +// DetectUnamePass detect a username / password question in a command +// ask is a function that gets executen when this function detect you need to fillin a password +// The ask argument will be "username" or "password" and expects the user's password or username back +func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) error { + ttyText := "" + errors := []error{} + err := c.RunCommandWithOutputLive(command, func(word string) string { + ttyText = ttyText + " " + word + + // detect username question + detectUname, err := regexp.MatchString(`Username\s*for\s*'.+':`, ttyText) + if err != nil { + errors = append(errors, err) + } + if detectUname { + // reset the text and return the user's username + ttyText = "" + return ask("username") + } + + // detect password question + detectPass, err := regexp.MatchString(`Password\s*for\s*'.+':`, ttyText) + if err != nil { + errors = append(errors, err) + } + if detectPass { + // reset the text and return the user's username + ttyText = "" + return ask("password") + } + return "" + }) + if err != nil { + return err + } + if len(errors) > 0 { + return errors[0] + } + return nil +} + // RunCommand runs a command and just returns the error func (c *OSCommand) RunCommand(command string) error { _, err := c.RunCommandWithOutput(command) From 9fc4262887d25697f39c9797041ce7705fb9ce7c Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 17 Oct 2018 20:41:22 +0200 Subject: [PATCH 002/118] small code addition --- pkg/commands/git.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index b9f4bee91..5f567038e 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -342,11 +342,12 @@ func (c *GitCommand) Push(branchName string, force bool) error { cmd := fmt.Sprintf("git push %s -u origin %s", forceFlag, branchName) return c.OSCommand.DetectUnamePass(cmd, func(passOrUname string) string { if passOrUname == "password" { - return "some password" // ask for password + return "some password" } if passOrUname == "username" { // ask for username + return "some username" } return "" }) From 01fa106de339a4340b6dbf847802a7b1efcc8359 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 17 Oct 2018 21:12:33 +0200 Subject: [PATCH 003/118] Added files to commit pannelsidjfjlisdjfilisldjfiljisldjilfjisdjilfjlsidMoved some code around --- pkg/commands/git.go | 14 ++------------ pkg/gui/files_panel.go | 10 +++++++++- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 5f567038e..5e2c179c7 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -333,24 +333,14 @@ func (c *GitCommand) Pull() error { } // Push pushes to a branch -func (c *GitCommand) Push(branchName string, force bool) error { +func (c *GitCommand) Push(branchName string, force bool, ask func(string) string) error { forceFlag := "" if force { forceFlag = "--force-with-lease " } cmd := fmt.Sprintf("git push %s -u origin %s", forceFlag, branchName) - return c.OSCommand.DetectUnamePass(cmd, func(passOrUname string) string { - if passOrUname == "password" { - // ask for password - return "some password" - } - if passOrUname == "username" { - // ask for username - return "some username" - } - return "" - }) + return c.OSCommand.DetectUnamePass(cmd, ask) } // SquashPreviousTwoCommits squashes a commit down to the one below it diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index a00bd2843..513aa336d 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -361,7 +361,15 @@ func (gui *Gui) pushWithForceFlag(currentView *gocui.View, force bool) error { } go func() { branchName := gui.State.Branches[0].Name - if err := gui.GitCommand.Push(branchName, force); err != nil { + err := gui.GitCommand.Push(branchName, force, func(passOrUname string) string { + if passOrUname == "password" { + // TODO: ask for password + return "some password" + } + // TODO: ask for username + return "some username" + }) + if err != nil { _ = gui.createErrorPanel(gui.g, err.Error()) } else { _ = gui.closeConfirmationPrompt(gui.g) From 4451cbc50b319205fc1bf162cdafa3f7609db141 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 17 Oct 2018 21:14:24 +0200 Subject: [PATCH 004/118] handled golangcibot --- pkg/commands/os.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 5e11844b6..ae7e9c325 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -92,7 +92,8 @@ func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) for scanner.Scan() { toWrite := output(re.ReplaceAllString(scanner.Text(), "")) if len(toWrite) > 0 { - tty.Write([]byte(toWrite + "\n")) + _, err := tty.Write([]byte(toWrite + "\n")) + logrus.Error(err.Error()) } } }() From 1a43d64de33d5565fb20d74dbac1c30a38ae880e Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 20 Oct 2018 16:25:48 +0200 Subject: [PATCH 005/118] Added extra translations --- pkg/i18n/dutch.go | 8 +++++++- pkg/i18n/english.go | 6 ++++++ pkg/i18n/polish.go | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index 6b24a5174..e79f59891 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -30,7 +30,13 @@ func addDutch(i18nObject *i18n.Bundle) error { Other: "Stash", }, &i18n.Message{ ID: "CommitMessage", - Other: "Commit Bericht", + Other: "Commit bericht", + }, &i18n.Message{ + ID: "PushUsername", + Other: "Gebruikers naam", + }, &i18n.Message{ + ID: "PushPassword", + Other: "Wachtwoord", }, &i18n.Message{ ID: "CommitChanges", Other: "Commit Veranderingen", diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 9ead5a54e..90bb97e8d 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -39,6 +39,12 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "CommitMessage", Other: "Commit message", + }, &i18n.Message{ + ID: "PushUsername", + Other: "Username", + }, &i18n.Message{ + ID: "PushPassword", + Other: "Password", }, &i18n.Message{ ID: "CommitChanges", Other: "commit changes", diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index c37bfe972..93bbece33 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -29,6 +29,12 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "CommitMessage", Other: "Wiadomość commita", + }, &i18n.Message{ + ID: "PushUsername", + Other: "Username", + }, &i18n.Message{ + ID: "PushPassword", + Other: "Password", }, &i18n.Message{ ID: "CommitChanges", Other: "commituj zmiany", From ac5696574c974f2482bd0f5b2ef5258df08ac53c Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 20 Oct 2018 17:18:42 +0200 Subject: [PATCH 006/118] added some translations --- pkg/i18n/dutch.go | 3 +++ pkg/i18n/english.go | 3 +++ pkg/i18n/polish.go | 3 +++ 3 files changed, 9 insertions(+) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index e79f59891..87b5d7a90 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -37,6 +37,9 @@ func addDutch(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "PushPassword", Other: "Wachtwoord", + }, &i18n.Message{ + ID: "PassUnameWrong", + Other: "Wachtwoord en/of gebruikersnaam verkeert", }, &i18n.Message{ ID: "CommitChanges", Other: "Commit Veranderingen", diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 90bb97e8d..b802597b3 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -45,6 +45,9 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "PushPassword", Other: "Password", + }, &i18n.Message{ + ID: "PassUnameWrong", + Other: "Password and/or username wrong", }, &i18n.Message{ ID: "CommitChanges", Other: "commit changes", diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index 93bbece33..d9f855148 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -35,6 +35,9 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "PushPassword", Other: "Password", + }, &i18n.Message{ + ID: "PassUnameWrong", + Other: "Password and/or username wrong", }, &i18n.Message{ ID: "CommitChanges", Other: "commituj zmiany", From 73a0a65ee15a638822748dd56d6bd33b8b1bb30f Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 20 Oct 2018 17:20:52 +0200 Subject: [PATCH 007/118] test --- pkg/i18n/english.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index b802597b3..54e7a4a7f 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -5,7 +5,6 @@ Todo list when making a new translation - Change the addEnglish() name to the language you want to translate to like addSomeLanguage() - change the first function argument of i18nObject.AddMessages( to the language you want to translate to like language.SomeLanguage - Remove this todo and the about section - */ package i18n From 727ba9f42ec0678a7da7124f8befc81550361019 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 20 Oct 2018 17:21:17 +0200 Subject: [PATCH 008/118] test --- pkg/i18n/english.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 54e7a4a7f..b802597b3 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -5,6 +5,7 @@ Todo list when making a new translation - Change the addEnglish() name to the language you want to translate to like addSomeLanguage() - change the first function argument of i18nObject.AddMessages( to the language you want to translate to like language.SomeLanguage - Remove this todo and the about section + */ package i18n From 12425f0aa760759d916ef16bbd028b2c2de5577b Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 20 Oct 2018 17:37:55 +0200 Subject: [PATCH 009/118] First good success --- pkg/commands/os.go | 3 +- pkg/gui/commit_message_panel.go | 66 +++++++++++++++++++++++++++++++++ pkg/gui/confirmation_panel.go | 1 + pkg/gui/files_panel.go | 19 +++++----- pkg/gui/gui.go | 14 +++++++ pkg/gui/keybindings.go | 10 +++++ pkg/gui/view_helpers.go | 9 ++++- 7 files changed, 109 insertions(+), 13 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index ae7e9c325..a64b286f4 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -92,8 +92,7 @@ func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) for scanner.Scan() { toWrite := output(re.ReplaceAllString(scanner.Text(), "")) if len(toWrite) > 0 { - _, err := tty.Write([]byte(toWrite + "\n")) - logrus.Error(err.Error()) + _, _ = tty.Write([]byte(toWrite + "\n")) } } }() diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index c26b5573a..329ccec62 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -3,6 +3,7 @@ package gui import ( "strconv" "strings" + "sync" "github.com/jesseduffield/gocui" ) @@ -51,6 +52,71 @@ func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { return gui.renderString(g, "options", message) } +var unamePassMessage = "" +var waitForGroup sync.WaitGroup +var waitForGroupActie = false + +// waitForPassUname wait for a username or password input from the pushPassUname popup +func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUname string) string { + pushPassUnameView := gui.getPushPassUnameView(g) + if passOrUname == "username" { + pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") + // pushPassUnameView.Mask = 1 + } else { + pushPassUnameView.Title = gui.Tr.SLocalize("PushPassword") + pushPassUnameView.Mask = '*' + } + g.Update(func(g *gocui.Gui) error { + g.SetViewOnTop("pushPassUname") + gui.switchFocus(g, currentView, pushPassUnameView) + gui.RenderCommitLength() + return nil + }) + waitForGroupActie = true + waitForGroup.Add(1) + waitForGroup.Wait() + + return unamePassMessage +} + +func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { + message := gui.trimmedContent(v) + unamePassMessage = message + if waitForGroupActie { + defer waitForGroup.Done() + } + gui.refreshFiles(g) + v.Clear() + v.SetCursor(0, 0) + g.SetViewOnBottom("pushPassUname") + gui.switchFocus(g, v, gui.getFilesView(g)) + return gui.refreshCommits(g) +} + +func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { + g.SetViewOnBottom("pushPassUname") + unamePassMessage = "" + if waitForGroupActie { + defer waitForGroup.Done() + } + return gui.switchFocus(g, v, gui.getFilesView(g)) +} + +func (gui *Gui) handlePushFocused(g *gocui.Gui, v *gocui.View) error { + if _, err := g.SetViewOnTop("pushPassUname"); err != nil { + return err + } + + message := gui.Tr.TemplateLocalize( + "CloseConfirm", + Teml{ + "keyBindClose": "esc", + "keyBindConfirm": "enter", + }, + ) + return gui.renderString(g, "options", message) +} + func (gui *Gui) simpleEditor(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { switch { case key == gocui.KeyBackspace || key == gocui.KeyBackspace2: diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index 58577e430..ef7c97e6e 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -85,6 +85,7 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt func (gui *Gui) onNewPopupPanel() { gui.g.SetViewOnBottom("commitMessage") + gui.g.SetViewOnBottom("pushPassUname") } func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 513aa336d..8083c0e6a 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -355,22 +355,21 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { return nil } -func (gui *Gui) pushWithForceFlag(currentView *gocui.View, force bool) error { +func (gui *Gui) pushWithForceFlag(g *gocui.Gui, currentView *gocui.View, force bool) error { if err := gui.createMessagePanel(gui.g, currentView, "", gui.Tr.SLocalize("PushWait")); err != nil { return err } go func() { branchName := gui.State.Branches[0].Name err := gui.GitCommand.Push(branchName, force, func(passOrUname string) string { - if passOrUname == "password" { - // TODO: ask for password - return "some password" - } - // TODO: ask for username - return "some username" + return gui.waitForPassUname(g, currentView, passOrUname) }) if err != nil { - _ = gui.createErrorPanel(gui.g, err.Error()) + errMessage := err.Error() + if errMessage == "exit status 128" { + errMessage = gui.Tr.SLocalize("PassUnameWrong") + } + _ = gui.createErrorPanel(gui.g, errMessage) } else { _ = gui.closeConfirmationPrompt(gui.g) _ = gui.refreshCommits(gui.g) @@ -384,10 +383,10 @@ func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error { // if we have pullables we'll ask if the user wants to force push _, pullables := gui.GitCommand.UpstreamDifferenceCount() if pullables == "?" || pullables == "0" { - return gui.pushWithForceFlag(v, false) + return gui.pushWithForceFlag(g, v, false) } err := gui.createConfirmationPanel(g, nil, gui.Tr.SLocalize("ForcePush"), gui.Tr.SLocalize("ForcePushPrompt"), func(g *gocui.Gui, v *gocui.View) error { - return gui.pushWithForceFlag(v, true) + return gui.pushWithForceFlag(g, v, true) }, nil) return err } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 7e7648cfe..2baf354c0 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -268,6 +268,20 @@ func (gui *Gui) layout(g *gocui.Gui) error { } } + if gui.getPushPassUnameView(g) == nil { + // doesn't matter where this view starts because it will be hidden + if pushPassUnameView, err := g.SetView("pushPassUname", 0, 0, width/2, height/2, 0); err != nil { + if err != gocui.ErrUnknownView { + return err + } + g.SetViewOnBottom("pushPassUname") + pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") + pushPassUnameView.FgColor = gocui.ColorWhite + pushPassUnameView.Editable = true + pushPassUnameView.Editor = gocui.EditorFunc(gui.simpleEditor) + } + } + if appStatusView, err := g.SetView("appStatus", -1, optionsTop, width, optionsTop+2, 0); err != nil { if err != gocui.ErrUnknownView { return err diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 78271a3f7..a30dba5f4 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -372,6 +372,16 @@ func (gui *Gui) GetKeybindings() []*Binding { Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: gui.handleCommitClose, + }, { + ViewName: "pushPassUname", + Key: gocui.KeyEnter, + Modifier: gocui.ModNone, + Handler: gui.handlePushConfirm, + }, { + ViewName: "pushPassUname", + Key: gocui.KeyEsc, + Modifier: gocui.ModNone, + Handler: gui.handlePushClose, }, { ViewName: "menu", Key: gocui.KeyEsc, diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 6c3e5505c..a5414448f 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -94,6 +94,8 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error { return nil case "commitMessage": return gui.handleCommitFocused(g, v) + case "pushPassUname": + return gui.handlePushFocused(g, v) case "main": // TODO: pull this out into a 'view focused' function gui.refreshMergePanel(g) @@ -288,6 +290,11 @@ func (gui *Gui) getCommitMessageView(g *gocui.Gui) *gocui.View { return v } +func (gui *Gui) getPushPassUnameView(g *gocui.Gui) *gocui.View { + v, _ := g.View("pushPassUname") + return v +} + func (gui *Gui) getBranchesView(g *gocui.Gui) *gocui.View { v, _ := g.View("branches") return v @@ -304,7 +311,7 @@ func (gui *Gui) currentViewName(g *gocui.Gui) string { func (gui *Gui) resizeCurrentPopupPanel(g *gocui.Gui) error { v := g.CurrentView() - if v.Name() == "commitMessage" || v.Name() == "confirmation" { + if v.Name() == "commitMessage" || v.Name() == "pushPassUname" || v.Name() == "confirmation" { return gui.resizePopupPanel(g, v) } return nil From b72841ca0cfd5f08f8352c4c597468bed715aff9 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 20 Oct 2018 18:58:37 +0200 Subject: [PATCH 010/118] Fixed golangcibot surgestions --- pkg/gui/commit_message_panel.go | 40 ++++++++++++++++++++++++--------- pkg/gui/gui.go | 5 ++++- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 329ccec62..39818a848 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -58,6 +58,8 @@ var waitForGroupActie = false // waitForPassUname wait for a username or password input from the pushPassUname popup func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUname string) string { + waitForGroupActie = true + waitForGroup.Add(1) pushPassUnameView := gui.getPushPassUnameView(g) if passOrUname == "username" { pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") @@ -67,15 +69,18 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn pushPassUnameView.Mask = '*' } g.Update(func(g *gocui.Gui) error { - g.SetViewOnTop("pushPassUname") - gui.switchFocus(g, currentView, pushPassUnameView) + _, err := g.SetViewOnTop("pushPassUname") + if err != nil { + return err + } + err = gui.switchFocus(g, currentView, pushPassUnameView) + if err != nil { + return err + } gui.RenderCommitLength() return nil }) - waitForGroupActie = true - waitForGroup.Add(1) waitForGroup.Wait() - return unamePassMessage } @@ -85,16 +90,31 @@ func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { if waitForGroupActie { defer waitForGroup.Done() } - gui.refreshFiles(g) + err := gui.refreshFiles(g) + if err != nil { + return err + } v.Clear() - v.SetCursor(0, 0) - g.SetViewOnBottom("pushPassUname") - gui.switchFocus(g, v, gui.getFilesView(g)) + err = v.SetCursor(0, 0) + if err != nil { + return err + } + _, err = g.SetViewOnBottom("pushPassUname") + if err != nil { + return err + } + err = gui.switchFocus(g, v, gui.getFilesView(g)) + if err != nil { + return err + } return gui.refreshCommits(g) } func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { - g.SetViewOnBottom("pushPassUname") + _, err := g.SetViewOnBottom("pushPassUname") + if err != nil { + return err + } unamePassMessage = "" if waitForGroupActie { defer waitForGroup.Done() diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 2baf354c0..681601d00 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -274,7 +274,10 @@ func (gui *Gui) layout(g *gocui.Gui) error { if err != gocui.ErrUnknownView { return err } - g.SetViewOnBottom("pushPassUname") + _, err := g.SetViewOnBottom("pushPassUname") + if err != nil { + return err + } pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") pushPassUnameView.FgColor = gocui.ColorWhite pushPassUnameView.Editable = true From 1be44eae84a3ec86adf749356c21d752d673223e Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 20 Oct 2018 19:03:51 +0200 Subject: [PATCH 011/118] Fixed surgestion from golangcibot --- pkg/gui/confirmation_panel.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index ef7c97e6e..ed0b0455a 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -7,6 +7,7 @@ package gui import ( + "log" "strings" "github.com/fatih/color" @@ -84,8 +85,14 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt } func (gui *Gui) onNewPopupPanel() { - gui.g.SetViewOnBottom("commitMessage") - gui.g.SetViewOnBottom("pushPassUname") + _, err := gui.g.SetViewOnBottom("commitMessage") + if err != nil { + log.Fatal(err) + } + _, err = gui.g.SetViewOnBottom("pushPassUname") + if err != nil { + log.Fatal(err) + } } func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { From ac03665df34e8be89672fb98e202b182d745fdaf Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 20 Oct 2018 19:44:56 +0200 Subject: [PATCH 012/118] Handled some errors --- pkg/commands/os.go | 1 + pkg/gui/commit_message_panel.go | 16 +++++++++++++--- pkg/gui/files_panel.go | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index a64b286f4..7bd714157 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -85,6 +85,7 @@ func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) go func() { // Regex to cleanup the command output + // sometimes the output words include unneeded spaces at eatch end of the string re := regexp.MustCompile(`(^\s*)|(\s*$)`) scanner := bufio.NewScanner(tty) diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 39818a848..3f2225a7b 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -60,10 +60,11 @@ var waitForGroupActie = false func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUname string) string { waitForGroupActie = true waitForGroup.Add(1) + pushPassUnameView := gui.getPushPassUnameView(g) if passOrUname == "username" { pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") - // pushPassUnameView.Mask = 1 + pushPassUnameView.Mask = 0 } else { pushPassUnameView.Title = gui.Tr.SLocalize("PushPassword") pushPassUnameView.Mask = '*' @@ -80,15 +81,23 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn gui.RenderCommitLength() return nil }) + + // wait for username/passwords input waitForGroup.Wait() return unamePassMessage } func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { message := gui.trimmedContent(v) + if message == "" { + // make sure to input something + // if not dune the push progress will run forever + message = "-" + } unamePassMessage = message if waitForGroupActie { - defer waitForGroup.Done() + waitForGroup.Done() + waitForGroupActie = false } err := gui.refreshFiles(g) if err != nil { @@ -117,7 +126,8 @@ func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { } unamePassMessage = "" if waitForGroupActie { - defer waitForGroup.Done() + waitForGroup.Done() + waitForGroupActie = false } return gui.switchFocus(g, v, gui.getFilesView(g)) } diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 8083c0e6a..ac5a8412e 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -364,6 +364,7 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, currentView *gocui.View, force b err := gui.GitCommand.Push(branchName, force, func(passOrUname string) string { return gui.waitForPassUname(g, currentView, passOrUname) }) + _, _ = g.SetViewOnBottom("pushPassUname") if err != nil { errMessage := err.Error() if errMessage == "exit status 128" { From e47c597b3a2d1101984bd7f402a0ca761fbf7169 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 14:35:07 +0200 Subject: [PATCH 013/118] Made it possible to build again for windows --- pkg/commands/exec_live_default.go | 57 +++++++++++++++++++++++++++++++ pkg/commands/exec_live_win.go | 8 +++++ pkg/commands/os.go | 46 ++----------------------- 3 files changed, 67 insertions(+), 44 deletions(-) create mode 100644 pkg/commands/exec_live_default.go create mode 100644 pkg/commands/exec_live_win.go diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go new file mode 100644 index 000000000..41ce3a995 --- /dev/null +++ b/pkg/commands/exec_live_default.go @@ -0,0 +1,57 @@ +// +build !windows + +package commands + +import ( + "bufio" + "os/exec" + "regexp" + + "github.com/ionrock/procs" + "github.com/kr/pty" + "github.com/mgutz/str" +) + +// RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout +// Output is a function that executes by every word that gets read by bufio +// As return of output you need to give a string that will be written to stdin +// NOTE: If the return data is empty it won't written anything to stdin +// NOTE: You don't have to include a enter in the return data this function will do that for you +func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { + splitCmd := str.ToArgv(command) + cmd := exec.Command(splitCmd[0], splitCmd[1:]...) + + cmd.Env = procs.Env(map[string]string{ + "LANG": "en_US.utf8", + "LC_ALL": "en_US.UTF-8", + }, true) + + tty, err := pty.Start(cmd) + + if err != nil { + return err + } + + defer func() { _ = tty.Close() }() + + go func() { + // Regex to cleanup the command output + // sometimes the output words include unneeded spaces at eatch end of the string + re := regexp.MustCompile(`(^\s*)|(\s*$)`) + + scanner := bufio.NewScanner(tty) + scanner.Split(bufio.ScanWords) + for scanner.Scan() { + toWrite := output(re.ReplaceAllString(scanner.Text(), "")) + if len(toWrite) > 0 { + _, _ = tty.Write([]byte(toWrite + "\n")) + } + } + }() + + if err := cmd.Wait(); err != nil { + return err + } + + return nil +} diff --git a/pkg/commands/exec_live_win.go b/pkg/commands/exec_live_win.go new file mode 100644 index 000000000..419eef5f2 --- /dev/null +++ b/pkg/commands/exec_live_win.go @@ -0,0 +1,8 @@ +// +build windows + +package commands + +// RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there +func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { + return c.RunCommand(command) +} diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 7bd714157..e99ffbbb7 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -1,17 +1,14 @@ package commands import ( - "bufio" "errors" "os" "os/exec" "regexp" "strings" - "github.com/ionrock/procs" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/utils" - "github.com/kr/pty" "github.com/mgutz/str" @@ -61,48 +58,9 @@ func (c *OSCommand) RunCommandWithOutput(command string) (string, error) { ) } -// RunCommandWithOutputLive runs a command and return every word that gets written in stdout -// Output is a function that executes by every word that gets read by bufio -// As return of output you need to give a string that will be written to stdin -// NOTE: If the return data is empty it won't written anything to stdin -// NOTE: You don't have to include a enter in the return data this function will do that for you +// RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) error { - splitCmd := str.ToArgv(command) - cmd := exec.Command(splitCmd[0], splitCmd[1:]...) - - cmd.Env = procs.Env(map[string]string{ - "LANG": "en_US.utf8", - "LC_ALL": "en_US.UTF-8", - }, true) - - tty, err := pty.Start(cmd) - - if err != nil { - return err - } - - defer func() { _ = tty.Close() }() - - go func() { - // Regex to cleanup the command output - // sometimes the output words include unneeded spaces at eatch end of the string - re := regexp.MustCompile(`(^\s*)|(\s*$)`) - - scanner := bufio.NewScanner(tty) - scanner.Split(bufio.ScanWords) - for scanner.Scan() { - toWrite := output(re.ReplaceAllString(scanner.Text(), "")) - if len(toWrite) > 0 { - _, _ = tty.Write([]byte(toWrite + "\n")) - } - } - }() - - if err := cmd.Wait(); err != nil { - return err - } - - return nil + return RunCommandWithOutputLiveWrapper(c, command, output) } // DetectUnamePass detect a username / password question in a command From 87d1b9a5470b3cdd6095714cceec39dcd7fd2093 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 14:56:15 +0200 Subject: [PATCH 014/118] Fixed circleci build --- pkg/commands/exec_live_win.go | 1 + vendor/github.com/ionrock/procs/Gopkg.toml | 30 ++ vendor/github.com/ionrock/procs/LICENSE.md | 13 + vendor/github.com/ionrock/procs/Makefile | 32 ++ vendor/github.com/ionrock/procs/README.md | 168 ++++++++++ vendor/github.com/ionrock/procs/builder.go | 58 ++++ .../github.com/ionrock/procs/builder_test.go | 58 ++++ vendor/github.com/ionrock/procs/env.go | 48 +++ vendor/github.com/ionrock/procs/env_test.go | 83 +++++ .../ionrock/procs/example_basic_test.go | 33 ++ .../procs/example_predefined_cmds_test.go | 23 ++ .../github.com/ionrock/procs/examples_test.go | 43 +++ vendor/github.com/ionrock/procs/manager.go | 119 +++++++ .../github.com/ionrock/procs/manager_test.go | 55 ++++ vendor/github.com/ionrock/procs/parse.go | 36 ++ vendor/github.com/ionrock/procs/parse_test.go | 44 +++ vendor/github.com/ionrock/procs/process.go | 307 ++++++++++++++++++ .../github.com/ionrock/procs/process_test.go | 143 ++++++++ vendor/github.com/kr/pty/License | 23 ++ vendor/github.com/kr/pty/README.md | 100 ++++++ vendor/github.com/kr/pty/doc.go | 16 + vendor/github.com/kr/pty/ioctl.go | 13 + vendor/github.com/kr/pty/ioctl_bsd.go | 39 +++ vendor/github.com/kr/pty/mktypes.bash | 19 ++ vendor/github.com/kr/pty/pty_darwin.go | 65 ++++ vendor/github.com/kr/pty/pty_dragonfly.go | 80 +++++ vendor/github.com/kr/pty/pty_freebsd.go | 78 +++++ vendor/github.com/kr/pty/pty_linux.go | 51 +++ vendor/github.com/kr/pty/pty_openbsd.go | 33 ++ vendor/github.com/kr/pty/pty_unsupported.go | 11 + vendor/github.com/kr/pty/run.go | 34 ++ vendor/github.com/kr/pty/types.go | 10 + vendor/github.com/kr/pty/types_dragonfly.go | 17 + vendor/github.com/kr/pty/types_freebsd.go | 15 + vendor/github.com/kr/pty/types_openbsd.go | 14 + vendor/github.com/kr/pty/util.go | 64 ++++ vendor/github.com/kr/pty/ztypes_386.go | 9 + vendor/github.com/kr/pty/ztypes_amd64.go | 9 + vendor/github.com/kr/pty/ztypes_arm.go | 9 + vendor/github.com/kr/pty/ztypes_arm64.go | 11 + .../kr/pty/ztypes_dragonfly_amd64.go | 14 + .../github.com/kr/pty/ztypes_freebsd_386.go | 13 + .../github.com/kr/pty/ztypes_freebsd_amd64.go | 14 + .../github.com/kr/pty/ztypes_freebsd_arm.go | 13 + vendor/github.com/kr/pty/ztypes_mipsx.go | 12 + .../github.com/kr/pty/ztypes_openbsd_386.go | 13 + .../github.com/kr/pty/ztypes_openbsd_amd64.go | 13 + vendor/github.com/kr/pty/ztypes_ppc64.go | 11 + vendor/github.com/kr/pty/ztypes_ppc64le.go | 11 + vendor/github.com/kr/pty/ztypes_s390x.go | 11 + vendor/vendor.json | 19 ++ 51 files changed, 2158 insertions(+) create mode 100644 vendor/github.com/ionrock/procs/Gopkg.toml create mode 100644 vendor/github.com/ionrock/procs/LICENSE.md create mode 100644 vendor/github.com/ionrock/procs/Makefile create mode 100644 vendor/github.com/ionrock/procs/README.md create mode 100644 vendor/github.com/ionrock/procs/builder.go create mode 100644 vendor/github.com/ionrock/procs/builder_test.go create mode 100644 vendor/github.com/ionrock/procs/env.go create mode 100644 vendor/github.com/ionrock/procs/env_test.go create mode 100644 vendor/github.com/ionrock/procs/example_basic_test.go create mode 100644 vendor/github.com/ionrock/procs/example_predefined_cmds_test.go create mode 100644 vendor/github.com/ionrock/procs/examples_test.go create mode 100644 vendor/github.com/ionrock/procs/manager.go create mode 100644 vendor/github.com/ionrock/procs/manager_test.go create mode 100644 vendor/github.com/ionrock/procs/parse.go create mode 100644 vendor/github.com/ionrock/procs/parse_test.go create mode 100644 vendor/github.com/ionrock/procs/process.go create mode 100644 vendor/github.com/ionrock/procs/process_test.go create mode 100644 vendor/github.com/kr/pty/License create mode 100644 vendor/github.com/kr/pty/README.md create mode 100644 vendor/github.com/kr/pty/doc.go create mode 100644 vendor/github.com/kr/pty/ioctl.go create mode 100644 vendor/github.com/kr/pty/ioctl_bsd.go create mode 100755 vendor/github.com/kr/pty/mktypes.bash create mode 100644 vendor/github.com/kr/pty/pty_darwin.go create mode 100644 vendor/github.com/kr/pty/pty_dragonfly.go create mode 100644 vendor/github.com/kr/pty/pty_freebsd.go create mode 100644 vendor/github.com/kr/pty/pty_linux.go create mode 100644 vendor/github.com/kr/pty/pty_openbsd.go create mode 100644 vendor/github.com/kr/pty/pty_unsupported.go create mode 100644 vendor/github.com/kr/pty/run.go create mode 100644 vendor/github.com/kr/pty/types.go create mode 100644 vendor/github.com/kr/pty/types_dragonfly.go create mode 100644 vendor/github.com/kr/pty/types_freebsd.go create mode 100644 vendor/github.com/kr/pty/types_openbsd.go create mode 100644 vendor/github.com/kr/pty/util.go create mode 100644 vendor/github.com/kr/pty/ztypes_386.go create mode 100644 vendor/github.com/kr/pty/ztypes_amd64.go create mode 100644 vendor/github.com/kr/pty/ztypes_arm.go create mode 100644 vendor/github.com/kr/pty/ztypes_arm64.go create mode 100644 vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go create mode 100644 vendor/github.com/kr/pty/ztypes_freebsd_386.go create mode 100644 vendor/github.com/kr/pty/ztypes_freebsd_amd64.go create mode 100644 vendor/github.com/kr/pty/ztypes_freebsd_arm.go create mode 100644 vendor/github.com/kr/pty/ztypes_mipsx.go create mode 100644 vendor/github.com/kr/pty/ztypes_openbsd_386.go create mode 100644 vendor/github.com/kr/pty/ztypes_openbsd_amd64.go create mode 100644 vendor/github.com/kr/pty/ztypes_ppc64.go create mode 100644 vendor/github.com/kr/pty/ztypes_ppc64le.go create mode 100644 vendor/github.com/kr/pty/ztypes_s390x.go create mode 100644 vendor/vendor.json diff --git a/pkg/commands/exec_live_win.go b/pkg/commands/exec_live_win.go index 419eef5f2..343e0dd9f 100644 --- a/pkg/commands/exec_live_win.go +++ b/pkg/commands/exec_live_win.go @@ -3,6 +3,7 @@ package commands // RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there +// TODO: Remove this hack and replace it with a propper way to run commands live on windows func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { return c.RunCommand(command) } diff --git a/vendor/github.com/ionrock/procs/Gopkg.toml b/vendor/github.com/ionrock/procs/Gopkg.toml new file mode 100644 index 000000000..2521101cd --- /dev/null +++ b/vendor/github.com/ionrock/procs/Gopkg.toml @@ -0,0 +1,30 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + branch = "master" + name = "github.com/apoydence/onpar" + +[[constraint]] + branch = "master" + name = "github.com/flynn/go-shlex" diff --git a/vendor/github.com/ionrock/procs/LICENSE.md b/vendor/github.com/ionrock/procs/LICENSE.md new file mode 100644 index 000000000..9fb415ad4 --- /dev/null +++ b/vendor/github.com/ionrock/procs/LICENSE.md @@ -0,0 +1,13 @@ +Copyright 2017 Eric Larson + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/ionrock/procs/Makefile b/vendor/github.com/ionrock/procs/Makefile new file mode 100644 index 000000000..9e82d411d --- /dev/null +++ b/vendor/github.com/ionrock/procs/Makefile @@ -0,0 +1,32 @@ +SOURCEDIR=. +SOURCES := $(shell find $(SOURCEDIR) -name '*.go') + +test: dep + dep ensure + go test . + +race: dep + dep ensure + go test -race . + + +dep: +ifeq (, $(shell which dep)) + go get -u github.com/golang/dep/cmd/dep +endif + +all: prelog cmdtmpl procmon + +prelog: $(SOURCES) + go build ./cmd/prelog + +cmdtmpl: $(SOURCES) + go build ./cmd/cmdtmpl + +procmon: $(SOURCES) + go build ./cmd/procmon + +clean: + rm -f prelog + rm -f cmdtmpl + rm -f procmon diff --git a/vendor/github.com/ionrock/procs/README.md b/vendor/github.com/ionrock/procs/README.md new file mode 100644 index 000000000..0efd1252b --- /dev/null +++ b/vendor/github.com/ionrock/procs/README.md @@ -0,0 +1,168 @@ +# Procs + +[![](https://travis-ci.org/ionrock/procs.svg?branch=master)](https://travis-ci.org/ionrock/procs) +[![Go Report Card](https://goreportcard.com/badge/github.com/ionrock/procs)](https://goreportcard.com/report/github.com/ionrock/procs) +[![GoDoc](https://godoc.org/github.com/ionrock/procs?status.svg)](https://godoc.org/github.com/ionrock/procs) + +Procs is a library to make working with command line applications a +little nicer. + +The primary use case is when you have to use a command line client in +place of an API. Often times you want to do things like output stdout +within your own logs or ensure that every time the command is called, +there are a standard set of flags that are used. + +## Basic Usage + +The majority of this functionality is intended to be included the +procs.Process. + +### Defining a Command + +A command can be defined by a string rather than a []string. Normally, +this also implies that the library will run the command in a shell, +exposing a potential man in the middle attack. Rather than using a +shell, procs [lexically +parses](https://github.com/flynn-archive/go-shlex) the command for the +different arguments. It also allows for pipes in order to string +commands together. + +```go +p := procs.NewProcess("kubectl get events | grep dev") +``` + +You can also define a new `Process` by passing in predefined commands. + +```go +cmds := []*exec.Cmd{ + exec.Command("kubectl", "get", "events"), + exec.Command("grep", "dev"), +} + +p := procs.Process{Cmds: cmds} +``` + +### Output Handling + +One use case that is cumbersome is using the piped output from a +command. For example, lets say we wanted to start a couple commands +and have each command have its own prefix in stdout, while still +capturing the output of the command as-is. + +```go +p := procs.NewProcess("cmd1") +p.OutputHandler = func(line string) string { + fmt.Printf("cmd1 | %s\n") + return line +} +out, _ := p.Run() +fmt.Println(out) +``` + +Whatever is returned from the `OutputHandler` will be in the buffered +output. In this way you can choose to filter or skip output buffering +completely. + +You can also define a `ErrHandler` using the same signature to get the +same filtering for stderr. + +### Environment Variables + +Rather than use the `exec.Cmd` `[]string` environment variables, a +`procs.Process` uses a `map[string]string` for environment variables. + +```go +p := procs.NewProcess("echo $FOO") +p.Env = map[string]string{"FOO": "foo"} +``` + +Also, environment variables defined by the `Process.Env` can be +expanded automatically using the `os.Expand` semantics and the +provided environment. + +There is a `ParseEnv` function that can help to merge the parent +processes' environment with any new values. + +```go +env := ParseEnv(os.Environ()) +env["USER"] = "foo" +``` + +Finally, if you are building commands manually, the `Env` function can +take a `map[string]string` and convert it to a `[]string` for use with +an `exec.Cmd`. The `Env` function also accepts a `useEnv` bool to help +include the parent process environment. + +```go +cmd := exec.Command("knife", "cookbook", "show", cb) +cmd.Env = Env(map[string]string{"USER": "knife-user"}, true) +``` + +## Example Applications + +Take a look in the [`cmd`](./cmd/) dir for some simple applications +that use the library. You can also `make all` to build them. The +examples below assume you've built them locally. + +### Prelog + +The `prelog` command allows running a command and prefixing the output +with a value. + +```bash +$ ./prelog -prefix foo -- echo 'hello world!' +Running the command +foo | hello world! +Accessing the output without a prefix. +hello world! +Running the command with Start / Wait +foo | hello world! +``` + +### Cmdtmpl + +The `cmdtmpl` command uses the `procs.Builder` to create a command +based on some paramters. It will take a `data.yml` file and +`template.yml` file to create a command. + +```bash +$ cat example/data.json +{ + "source": "https://my.example.org", + "user": "foo", + "model": "widget", + "action": "create", + "args": "-f new -i improved" +} +$ cat example/template.json +[ + "mysvc ${model} ${action} ${args}", + "--endpoint ${source}", + "--username ${user}" +] +$ ./cmdtmpl -data example/data.json -template example/template.json +Command: mysvc foo widget create -f new -i imporoved --endpoint https://my.example.org --username foo +$ ./cmdtmpl -data example/data.json -template example/template.json -field user=bar +Command: mysvc foo widget create -f new -i imporoved --endpoint https://my.example.org --username bar +``` + +### Procmon + +The `procmon` command acts like +[foreman](https://github.com/ddollar/foreman) with the difference +being it uses a JSON file with key value pairs instead of a +Procfile. This example uses the `procs.Manager` to manage a set of +`procs.Processes`. + +```bash +$ cat example/procfile.json +{ + "web": "python -m SimpleHTTPServer" +} +$ ./procmon -procfile example/procfile.json +web | Starting web with python -m SimpleHTTPServer +``` + +You can then access http://localhost:8000 to see the logs. You can +also kill the child process and see `procmon` recognizing it has +exited and exit itself. diff --git a/vendor/github.com/ionrock/procs/builder.go b/vendor/github.com/ionrock/procs/builder.go new file mode 100644 index 000000000..85c68391c --- /dev/null +++ b/vendor/github.com/ionrock/procs/builder.go @@ -0,0 +1,58 @@ +package procs + +import ( + "os" + "strings" +) + +// Builder helps construct commands using templates. +type Builder struct { + Context map[string]string + Templates []string +} + +func (b *Builder) getConfig(ctx map[string]string) func(string) string { + return func(key string) string { + if v, ok := ctx[key]; ok { + return v + } + return "" + } +} + +func (b *Builder) expand(v string, ctx map[string]string) string { + return os.Expand(v, b.getConfig(ctx)) +} + +// Command returns the result of the templates as a single string. +func (b *Builder) Command() string { + parts := []string{} + for _, t := range b.Templates { + parts = append(parts, b.expand(t, b.Context)) + } + + return strings.Join(parts, " ") +} + +// CommandContext returns the result of the templates as a single +// string, but allows providing an environment context as a +// map[string]string for expansions. +func (b *Builder) CommandContext(ctx map[string]string) string { + // Build our environment context by starting with our Builder + // context and overlay the passed in context map. + env := make(map[string]string) + for k, v := range b.Context { + env[k] = b.expand(v, b.Context) + } + + for k, v := range ctx { + env[k] = b.expand(v, env) + } + + parts := []string{} + for _, t := range b.Templates { + parts = append(parts, b.expand(t, env)) + } + + return strings.Join(parts, " ") +} diff --git a/vendor/github.com/ionrock/procs/builder_test.go b/vendor/github.com/ionrock/procs/builder_test.go new file mode 100644 index 000000000..a093eeb09 --- /dev/null +++ b/vendor/github.com/ionrock/procs/builder_test.go @@ -0,0 +1,58 @@ +package procs_test + +import ( + "testing" + + "github.com/ionrock/procs" +) + +func TestCommandContext(t *testing.T) { + b := &procs.Builder{ + Context: map[string]string{ + "options": "-Fj -s https://example.com/chef -k knife.pem", + }, + + Templates: []string{ + "knife", + "${model} ${action}", + "${args}", + "${options}", + }, + } + + cmd := b.CommandContext(map[string]string{ + "model": "data bag", + "action": "from file", + "args": "foo data_bags/foo/bar.json", + }) + + expected := "knife data bag from file foo data_bags/foo/bar.json -Fj -s https://example.com/chef -k knife.pem" + if cmd != expected { + t.Fatalf("failed building command: %q != %q", cmd, expected) + } +} + +func TestCommand(t *testing.T) { + b := &procs.Builder{ + Context: map[string]string{ + "options": "-Fj -s https://example.com/chef -k knife.pem", + "model": "data bag", + "action": "from file", + "args": "foo data_bags/foo/bar.json", + }, + + Templates: []string{ + "knife", + "${model} ${action}", + "${args}", + "${options}", + }, + } + + cmd := b.CommandContext(map[string]string{}) + + expected := "knife data bag from file foo data_bags/foo/bar.json -Fj -s https://example.com/chef -k knife.pem" + if cmd != expected { + t.Fatalf("failed building command: %q != %q", cmd, expected) + } +} diff --git a/vendor/github.com/ionrock/procs/env.go b/vendor/github.com/ionrock/procs/env.go new file mode 100644 index 000000000..3cf93f76d --- /dev/null +++ b/vendor/github.com/ionrock/procs/env.go @@ -0,0 +1,48 @@ +package procs + +import ( + "fmt" + "os" + "strings" +) + +// ParseEnv takes an environment []string and converts it to a map[string]string. +func ParseEnv(environ []string) map[string]string { + env := make(map[string]string) + for _, e := range environ { + pair := strings.SplitN(e, "=", 2) + + // There is a chance we can get an env with empty values + if len(pair) == 2 { + env[pair[0]] = pair[1] + } + } + return env +} + +// Env takes a map[string]string and converts it to a []string that +// can be used with exec.Cmd. The useEnv boolean flag will include the +// current process environment, overlaying the provided env +// map[string]string. +func Env(env map[string]string, useEnv bool) []string { + envlist := []string{} + + // update our env by loading our env and overriding any values in + // the provided env. + if useEnv { + environ := ParseEnv(os.Environ()) + for k, v := range env { + environ[k] = v + } + env = environ + } + + for key, val := range env { + if key == "" { + continue + } + envlist = append(envlist, fmt.Sprintf("%s=%s", key, val)) + } + + return envlist +} diff --git a/vendor/github.com/ionrock/procs/env_test.go b/vendor/github.com/ionrock/procs/env_test.go new file mode 100644 index 000000000..1b2a0498b --- /dev/null +++ b/vendor/github.com/ionrock/procs/env_test.go @@ -0,0 +1,83 @@ +package procs_test + +import ( + "fmt" + "os" + "os/exec" + "strings" + "testing" + + "github.com/ionrock/procs" +) + +func TestParseEnv(t *testing.T) { + env := []string{ + "FOO=bar", + "BAZ=`echo 'hello=world'`", + } + + m := procs.ParseEnv(env) + + v, ok := m["FOO"] + if !ok { + t.Errorf("error missing FOO from env: %#v", m) + } + + if v != "bar" { + t.Errorf("error FOO != bar: %s", v) + } + + v, ok = m["BAZ"] + + if !ok { + t.Errorf("error missing BAZ from env: %#v", m) + } + + expectBaz := "`echo 'hello=world'`" + if v != expectBaz { + t.Errorf("error BAZ != %s: %s", expectBaz, v) + } +} + +func TestEnvBuilder(t *testing.T) { + env := procs.Env(map[string]string{ + "FOO": "bar", + "BAZ": "hello world", + }, false) + + if len(env) != 2 { + t.Errorf("error loading env: %s", env) + } +} + +func helperEnvCommand(env map[string]string) *exec.Cmd { + cmd := exec.Command(os.Args[0], "-test.run=TestEnvBuilderOverrides") + cmd.Env = procs.Env(env, false) + return cmd +} + +func TestEnvBuilderOverrides(t *testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + for _, envvar := range procs.Env(map[string]string{"FOO": "override"}, true) { + fmt.Println(envvar) + } +} + +func TestEnvBuilderWithEnv(t *testing.T) { + cmd := helperEnvCommand(map[string]string{ + "GO_WANT_HELPER_PROCESS": "1", + "FOO": "default", + }) + out, err := cmd.Output() + if err != nil { + t.Fatalf("error running helper: %s", err) + } + + env := procs.ParseEnv(strings.Split(string(out), "\n")) + + if env["FOO"] != "override" { + t.Errorf("error overriding envvar: %s", string(out)) + } +} diff --git a/vendor/github.com/ionrock/procs/example_basic_test.go b/vendor/github.com/ionrock/procs/example_basic_test.go new file mode 100644 index 000000000..8d2adaf06 --- /dev/null +++ b/vendor/github.com/ionrock/procs/example_basic_test.go @@ -0,0 +1,33 @@ +package procs_test + +import ( + "fmt" + + "github.com/ionrock/procs" +) + +func Example() { + + b := procs.Builder{ + Context: map[string]string{ + "NAME": "eric", + }, + Templates: []string{ + "echo $NAME |", + "grep $NAME", + }, + } + + cmd := b.Command() + + fmt.Println(cmd) + + p := procs.NewProcess(cmd) + + p.Run() + out, _ := p.Output() + fmt.Println(string(out)) + // Output: + // echo eric | grep eric + // eric +} diff --git a/vendor/github.com/ionrock/procs/example_predefined_cmds_test.go b/vendor/github.com/ionrock/procs/example_predefined_cmds_test.go new file mode 100644 index 000000000..3dfd64015 --- /dev/null +++ b/vendor/github.com/ionrock/procs/example_predefined_cmds_test.go @@ -0,0 +1,23 @@ +package procs_test + +import ( + "fmt" + "os/exec" + + "github.com/ionrock/procs" +) + +func Example_predefinedCmds() { + p := procs.Process{ + Cmds: []*exec.Cmd{ + exec.Command("echo", "foo"), + exec.Command("grep", "foo"), + }, + } + + p.Run() + out, _ := p.Output() + fmt.Println(string(out)) + // Output: + // foo +} diff --git a/vendor/github.com/ionrock/procs/examples_test.go b/vendor/github.com/ionrock/procs/examples_test.go new file mode 100644 index 000000000..c9b43cc1c --- /dev/null +++ b/vendor/github.com/ionrock/procs/examples_test.go @@ -0,0 +1,43 @@ +package procs_test + +import ( + "fmt" + + "github.com/ionrock/procs" +) + +func ExampleSplitCommand() { + parts := procs.SplitCommand("echo 'hello world'") + for i, p := range parts { + fmt.Printf("%d %s\n", i+1, p) + } + + // Output: + // 1 echo + // 2 hello world +} + +func ExampleSplitCommandEnv() { + env := map[string]string{ + "GREETING": "hello", + "NAME": "world!", + "PASSWORD": "secret", + } + + getenv := func(key string) string { + if v, ok := env[key]; ok && key != "PASSWORD" { + return v + } + return "" + } + + parts := procs.SplitCommandEnv("echo '$GREETING $NAME $PASSWORD'", getenv) + + for i, p := range parts { + fmt.Printf("%d %s\n", i+1, p) + } + + // Output: + // 1 echo + // 2 hello world! +} diff --git a/vendor/github.com/ionrock/procs/manager.go b/vendor/github.com/ionrock/procs/manager.go new file mode 100644 index 000000000..91169e300 --- /dev/null +++ b/vendor/github.com/ionrock/procs/manager.go @@ -0,0 +1,119 @@ +package procs + +import ( + "fmt" + "sync" +) + +// Manager manages a set of Processes. +type Manager struct { + Processes map[string]*Process + + lock sync.Mutex +} + +// NewManager creates a new *Manager. +func NewManager() *Manager { + return &Manager{ + Processes: make(map[string]*Process), + } + +} + +// StdoutHandler returns an OutHandler that will ensure the underlying +// process has an empty stdout buffer and logs to stdout a prefixed value +// of "$name | $line". +func (m *Manager) StdoutHandler(name string) OutHandler { + return func(line string) string { + fmt.Printf("%s | %s\n", name, line) + return "" + } +} + +// StderrHandler returns an OutHandler that will ensure the underlying +// process has an empty stderr buffer and logs to stdout a prefixed value +// of "$name | $line". +func (m *Manager) StderrHandler(name string) OutHandler { + return func(line string) string { + fmt.Printf("%s | %s\n", name, line) + return "" + } +} + +// Start and managed a new process using the default handlers from a +// string. +func (m *Manager) Start(name, cmd string) error { + m.lock.Lock() + defer m.lock.Unlock() + + p := NewProcess(cmd) + p.OutputHandler = m.StdoutHandler(name) + p.ErrHandler = m.StderrHandler(name) + err := p.Start() + if err != nil { + return err + } + + m.Processes[name] = p + return nil +} + +// StartProcess starts and manages a predifined process. +func (m *Manager) StartProcess(name string, p *Process) error { + m.lock.Lock() + defer m.lock.Unlock() + + err := p.Start() + if err != nil { + return err + } + + m.Processes[name] = p + return nil +} + +// Stop will try to stop a managed process. If the process does not +// exist, no error is returned. +func (m *Manager) Stop(name string) error { + p, ok := m.Processes[name] + // We don't mind stopping a process that doesn't exist. + if !ok { + return nil + } + + return p.Stop() +} + +// Remove will try to stop and remove a managed process. +func (m *Manager) Remove(name string) error { + m.lock.Lock() + defer m.lock.Unlock() + + err := m.Stop(name) + if err != nil { + return err + } + + // Note that if the stop fails we don't remove it from the map of + // processes to avoid losing the reference. + delete(m.Processes, name) + + return nil +} + +// Wait will block until all managed processes have finished. +func (m *Manager) Wait() error { + wg := &sync.WaitGroup{} + wg.Add(len(m.Processes)) + + for _, p := range m.Processes { + go func(proc *Process) { + defer wg.Done() + proc.Wait() + }(p) + } + + wg.Wait() + + return nil +} diff --git a/vendor/github.com/ionrock/procs/manager_test.go b/vendor/github.com/ionrock/procs/manager_test.go new file mode 100644 index 000000000..69c999407 --- /dev/null +++ b/vendor/github.com/ionrock/procs/manager_test.go @@ -0,0 +1,55 @@ +package procs_test + +import ( + "flag" + "fmt" + "log" + "net/http" + "os" + "testing" + + "github.com/ionrock/procs" +) + +func TestManagerStartHelper(t *testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + + port := flag.String("p", "12212", "port to serve on") + directory := flag.String("d", ".", "the directory of static file to host") + flag.Parse() + + http.Handle("/", http.FileServer(http.Dir(*directory))) + + log.Printf("Serving %s on HTTP port: %s\n", *directory, *port) + log.Fatal(http.ListenAndServe(":"+*port, nil)) + os.Exit(0) +} + +func TestManagerStart(t *testing.T) { + m := procs.NewManager() + + err := m.Start("test", fmt.Sprintf("%s -test.run=TestManagerStartHelper", os.Args[0])) + if err != nil { + t.Errorf("failed to start test process: %s", err) + } + + if len(m.Processes) != 1 { + t.Error("failed to add process") + } + + err = m.Stop("test") + if err != nil { + t.Errorf("error stopping process: %s", err) + } + + err = m.Remove("test") + if err != nil { + t.Errorf("error removing process: %s", err) + } + + if len(m.Processes) != 0 { + t.Error("failed to remove processes") + } +} diff --git a/vendor/github.com/ionrock/procs/parse.go b/vendor/github.com/ionrock/procs/parse.go new file mode 100644 index 000000000..8753f3878 --- /dev/null +++ b/vendor/github.com/ionrock/procs/parse.go @@ -0,0 +1,36 @@ +package procs + +import ( + "log" + "os" + "strings" + + shlex "github.com/flynn/go-shlex" +) + +// SplitCommand parses a command and splits it into lexical arguments +// like a shell, returning a []string that can be used as arguments to +// exec.Command. +func SplitCommand(cmd string) []string { + return SplitCommandEnv(cmd, nil) +} + +// SplitCommandEnv parses a command and splits it into lexical +// arguments like a shell, returning a []string that can be used as +// arguments to exec.Command. It also allows providing an expansion +// function that will be used when expanding values within the parsed +// arguments. +func SplitCommandEnv(cmd string, getenv func(key string) string) []string { + parts, err := shlex.Split(strings.TrimSpace(cmd)) + if err != nil { + log.Fatal(err) + } + + if getenv != nil { + for i, p := range parts { + parts[i] = os.Expand(p, getenv) + } + } + + return parts +} diff --git a/vendor/github.com/ionrock/procs/parse_test.go b/vendor/github.com/ionrock/procs/parse_test.go new file mode 100644 index 000000000..1e24f6a92 --- /dev/null +++ b/vendor/github.com/ionrock/procs/parse_test.go @@ -0,0 +1,44 @@ +package procs_test + +import ( + "testing" + + "github.com/apoydence/onpar" + . "github.com/apoydence/onpar/expect" + . "github.com/apoydence/onpar/matchers" + "github.com/ionrock/procs" +) + +func matchSplitCommand(t *testing.T, parts, expected []string) { + for i, part := range parts { + Expect(t, part).To(Equal(expected[i])) + } +} + +func TestSplitCommand(t *testing.T) { + o := onpar.New() + defer o.Run(t) + + o.Group("split with pipe", func() { + o.BeforeEach(func(t *testing.T) (*testing.T, []string, []string) { + parts := procs.SplitCommand("echo 'foo' | grep o") + expected := []string{"echo", "foo", "|", "grep", "o"} + return t, parts, expected + }) + + o.Spec("pass with a pipe", matchSplitCommand) + }) + + o.Group("replace with specific context", func() { + o.BeforeEach(func(t *testing.T) (*testing.T, []string, []string) { + parts := procs.SplitCommandEnv("echo ${FOO}", func(k string) string { + return "bar" + }) + + expected := []string{"echo", "bar"} + return t, parts, expected + }) + + o.Spec("expand values found in provided env", matchSplitCommand) + }) +} diff --git a/vendor/github.com/ionrock/procs/process.go b/vendor/github.com/ionrock/procs/process.go new file mode 100644 index 000000000..a990e9ff2 --- /dev/null +++ b/vendor/github.com/ionrock/procs/process.go @@ -0,0 +1,307 @@ +// Procs is a library to make working with command line applications a +// little nicer. +// +// The goal is to expand on the os/exec package by providing some +// features usually accomplished in a shell, without having to resort to +// a shell. Procs also tries to make working with output simpler by +// providing a simple line handler API over working with io pipes. +// +// Finally, while the hope is that procs provides some convenience, it +// is also a goal to help make it easier to write more secure +// code. For example, avoiding a shell and the ability to manage the +// environment as a map[string]string are both measures that intend to +// make it easier to accomplish things like avoiding outputting +// secrets and opening the door for MITM attacks. With that said, it is +// always important to consider the security implications, especially +// when you are working with untrusted input or sensitive data. +package procs + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "os" + "os/exec" + "sync" +) + +// OutHandler defines the interface for writing output handlers for +// Process objects. +type OutHandler func(string) string + +// Process is intended to be used like exec.Cmd where possible. +type Process struct { + // CmdString takes a string and parses it into the relevant cmds + CmdString string + + // Cmds is the list of command delmited by pipes. + Cmds []*exec.Cmd + + // Env provides a map[string]string that can mutated before + // running a command. + Env map[string]string + + // Dir defines the directory the command should run in. The + // Default is the current dir. + Dir string + + // OutputHandler can be defined to perform any sort of processing + // on the output. The simple interface is to accept a string (a + // line of output) and return a string that will be included in the + // buffered output and/or output written to stdout.' + // + // For example defining the Process as: + // + // prefix := "myapp" + // p := &procs.Process{ + // OutputHandler: func(line string) string { + // return fmt.Sprintf("%s | %s", prefix, line) + // }, + // } + // + // This would prefix the stdout lines with a "myapp | ". + // + // By the default, this function is nil and will be skipped, with + // the unchanged line being added to the respective output buffer. + OutputHandler OutHandler + + // ErrHandler is a OutputHandler for stderr. + ErrHandler OutHandler + + // When no output is given, we'll buffer output in these vars. + errBuffer bytes.Buffer + outBuffer bytes.Buffer + + // When a output handler is provided, we ensure we're handling a + // single line at at time. + outputWait *sync.WaitGroup +} + +// NewProcess creates a new *Process from a command string. +// +// It is assumed that the user will mutate the resulting *Process by +// setting the necessary attributes. +func NewProcess(command string) *Process { + return &Process{CmdString: command} +} + +// internal expand method to use the proc env. +func (p *Process) expand(s string) string { + return os.Expand(s, func(key string) string { + v, _ := p.Env[key] + return v + }) +} + +// addCmd adds a new command to the list of commands, ensuring the Dir +// and Env have been added to the underlying *exec.Cmd instances. +func (p *Process) addCmd(cmdparts []string) { + var cmd *exec.Cmd + if len(cmdparts) == 1 { + cmd = exec.Command(cmdparts[0]) + } else { + cmd = exec.Command(cmdparts[0], cmdparts[1:]...) + } + + if p.Dir != "" { + cmd.Dir = p.Dir + } + + if p.Env != nil { + env := []string{} + for k, v := range p.Env { + env = append(env, fmt.Sprintf("%s=%s", k, p.expand(v))) + } + + cmd.Env = env + } + + p.Cmds = append(p.Cmds, cmd) +} + +// findCmds parses the CmdString to find the commands that should be +// run by spliting the lexically parsed command by pipes ("|"). +func (p *Process) findCmds() { + // Skip if the cmd set is already set. This allows manual creation + // of piped commands. + if len(p.Cmds) > 0 { + return + } + + if p.CmdString == "" { + return + } + + parts := SplitCommand(p.CmdString) + for i := range parts { + parts[i] = p.expand(parts[i]) + } + + cmd := []string{} + for _, part := range parts { + if part == "|" { + p.addCmd(cmd) + cmd = []string{} + } else { + cmd = append(cmd, part) + } + } + + p.addCmd(cmd) +} + +// lineReader takes will read a line in the io.Reader and write to the +// Process output buffer and use any OutputHandler that exists. +func (p *Process) lineReader(wg *sync.WaitGroup, r io.Reader, w *bytes.Buffer, handler OutHandler) { + defer wg.Done() + + reader := bufio.NewReader(r) + var buffer bytes.Buffer + + for { + buf := make([]byte, 1024) + + n, err := reader.Read(buf) + if err != nil { + return + } + + buf = buf[:n] + + for { + i := bytes.IndexByte(buf, '\n') + if i < 0 { + break + } + + buffer.Write(buf[0:i]) + outLine := buffer.String() + if handler != nil { + outLine = handler(outLine) + } + w.WriteString(outLine) + buffer.Reset() + buf = buf[i+1:] + } + buffer.Write(buf) + } +} + +// checkErr shortens the creation of the pipes by bailing out with a +// log.Fatal. +func checkErr(msg string, err error) { + if err != nil { + log.Fatalf("%s: %s", msg, err) + } +} + +func (p *Process) setupPipes() error { + last := len(p.Cmds) - 1 + + if last != 0 { + for i, cmd := range p.Cmds[:last] { + var err error + + p.Cmds[i+1].Stdin, err = cmd.StdoutPipe() + if err != nil { + fmt.Printf("error creating stdout pipe: %s\n", err) + return err + } + + cmd.Stderr = &p.errBuffer + } + } + + cmd := p.Cmds[last] + stdout, err := cmd.StdoutPipe() + if err != nil { + fmt.Printf("error creating stdout pipe: %s\n", err) + return err + } + + stderr, err := cmd.StderrPipe() + if err != nil { + fmt.Printf("error creating stderr pipe: %s\n", err) + return err + } + + p.outputWait = new(sync.WaitGroup) + p.outputWait.Add(2) + + // These close the stdout/err channels + go p.lineReader(p.outputWait, stdout, &p.outBuffer, p.OutputHandler) + go p.lineReader(p.outputWait, stderr, &p.errBuffer, p.ErrHandler) + + return nil +} + +// Run executes the cmds and returns the output as a string and any error. +func (p *Process) Run() error { + if err := p.Start(); err != nil { + return err + } + + return p.Wait() +} + +// Start will start the list of cmds. +func (p *Process) Start() error { + p.findCmds() + p.setupPipes() + + for i, cmd := range p.Cmds { + err := cmd.Start() + if err != nil { + defer func() { + for _, precmd := range p.Cmds[0:i] { + precmd.Wait() + } + }() + return err + } + } + + return nil +} + +// Wait will block, waiting for the commands to finish. +func (p *Process) Wait() error { + if p.outputWait != nil { + p.outputWait.Wait() + } + + var err error + for _, cmd := range p.Cmds { + err = cmd.Wait() + } + return err +} + +// Stop tries to stop the process. +func (p *Process) Stop() error { + for _, cmd := range p.Cmds { + // ProcessState means it is already exited. + if cmd.ProcessState != nil { + continue + } + + err := cmd.Process.Kill() + if err != nil { + return err + } + } + + return nil +} + +// Output returns the buffered output as []byte. +func (p *Process) Output() ([]byte, error) { + return p.outBuffer.Bytes(), nil +} + +// ErrOutput returns the buffered stderr as []byte +func (p *Process) ErrOutput() ([]byte, error) { + return p.errBuffer.Bytes(), nil +} diff --git a/vendor/github.com/ionrock/procs/process_test.go b/vendor/github.com/ionrock/procs/process_test.go new file mode 100644 index 000000000..d66c5bd66 --- /dev/null +++ b/vendor/github.com/ionrock/procs/process_test.go @@ -0,0 +1,143 @@ +package procs_test + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "testing" + + "github.com/ionrock/procs" +) + +func newProcess() *procs.Process { + return &procs.Process{ + Cmds: []*exec.Cmd{ + exec.Command("echo", "foo"), + exec.Command("grep", "foo"), + }, + } +} + +func TestProcess(t *testing.T) { + p := newProcess() + + err := p.Run() + if err != nil { + t.Fatalf("error running program: %s", err) + } + + out, _ := p.Output() + if !bytes.Equal(bytes.TrimSpace(out), []byte("foo")) { + t.Errorf("wrong output: expected foo but got %s", out) + } +} + +func TestProcessWithOutput(t *testing.T) { + p := newProcess() + + p.OutputHandler = func(line string) string { + return fmt.Sprintf("x | %s", line) + } + + err := p.Run() + + if err != nil { + t.Fatalf("error running program: %s", err) + } + expected := []byte("x | foo") + out, _ := p.Output() + if !bytes.Equal(bytes.TrimSpace(out), expected) { + t.Errorf("wrong output: expected %q but got %q", expected, out) + } +} + +func TestProcessStartAndWait(t *testing.T) { + p := newProcess() + + p.Start() + p.Wait() + + out, _ := p.Output() + expected := []byte("foo") + if !bytes.Equal(bytes.TrimSpace(out), expected) { + t.Errorf("wrong output: expected %q but got %q", expected, out) + } +} + +func TestProcessStartAndWaitWithOutput(t *testing.T) { + p := newProcess() + p.OutputHandler = func(line string) string { + return fmt.Sprintf("x | %s", line) + } + + p.Start() + p.Wait() + + out, _ := p.Output() + expected := []byte("x | foo") + if !bytes.Equal(bytes.TrimSpace(out), expected) { + t.Errorf("wrong output: expected %q but got %q", expected, out) + } +} + +func TestProcessFromString(t *testing.T) { + p := procs.NewProcess("echo 'foo'") + err := p.Run() + if err != nil { + t.Fatalf("error running program: %s", err) + } + out, _ := p.Output() + if !bytes.Equal(bytes.TrimSpace(out), []byte("foo")) { + t.Errorf("wrong output: expected foo but got %s", out) + } +} + +func TestProcessFromStringWithPipe(t *testing.T) { + p := procs.NewProcess("echo 'foo' | grep foo") + err := p.Run() + if err != nil { + t.Fatalf("error running program: %s", err) + } + + out, _ := p.Output() + if !bytes.Equal(bytes.TrimSpace(out), []byte("foo")) { + t.Errorf("wrong output: expected foo but got %s", out) + } +} + +func TestStderrOutput(t *testing.T) { + if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { + return + } + + fmt.Fprintln(os.Stdout, "stdout output") + fmt.Fprintln(os.Stderr, "stderr output") + os.Exit(1) +} + +func TestProcessPipeWithFailures(t *testing.T) { + + // This will run a piped command with a failure part way + // through. We want to be sure we get output on stderr. + p := procs.NewProcess(fmt.Sprintf("echo 'foo' | %s -test.run=TestStderrOutput | grep foo", os.Args[0])) + p.Env = map[string]string{"GO_WANT_HELPER_PROCESS": "1"} + + err := p.Run() + if err == nil { + t.Fatal("expected error running program") + + } + + out, _ := p.Output() + expected := []byte("") // expecting no output b/c the grep foo won't run + if !bytes.Equal(out, expected) { + t.Errorf("wrong stdout output: expected '%s' but got '%s'", expected, out) + } + + errOut, _ := p.ErrOutput() + expected = []byte("stderr output") + if !bytes.Equal(bytes.TrimSpace(errOut), expected) { + t.Errorf("wrong stderr output: expected '%s' but got '%s'", expected, out) + } +} diff --git a/vendor/github.com/kr/pty/License b/vendor/github.com/kr/pty/License new file mode 100644 index 000000000..6b7558b6b --- /dev/null +++ b/vendor/github.com/kr/pty/License @@ -0,0 +1,23 @@ +Copyright (c) 2011 Keith Rarick + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall +be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/kr/pty/README.md b/vendor/github.com/kr/pty/README.md new file mode 100644 index 000000000..f9bb002e0 --- /dev/null +++ b/vendor/github.com/kr/pty/README.md @@ -0,0 +1,100 @@ +# pty + +Pty is a Go package for using unix pseudo-terminals. + +## Install + + go get github.com/kr/pty + +## Example + +### Command + +```go +package main + +import ( + "github.com/kr/pty" + "io" + "os" + "os/exec" +) + +func main() { + c := exec.Command("grep", "--color=auto", "bar") + f, err := pty.Start(c) + if err != nil { + panic(err) + } + + go func() { + f.Write([]byte("foo\n")) + f.Write([]byte("bar\n")) + f.Write([]byte("baz\n")) + f.Write([]byte{4}) // EOT + }() + io.Copy(os.Stdout, f) +} +``` + +### Shell + +```go +package main + +import ( + "io" + "log" + "os" + "os/exec" + "os/signal" + "syscall" + + "github.com/kr/pty" + "golang.org/x/crypto/ssh/terminal" +) + +func test() error { + // Create arbitrary command. + c := exec.Command("bash") + + // Start the command with a pty. + ptmx, err := pty.Start(c) + if err != nil { + return err + } + // Make sure to close the pty at the end. + defer func() { _ = ptmx.Close() }() // Best effort. + + // Handle pty size. + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGWINCH) + go func() { + for range ch { + if err := pty.InheritSize(os.Stdin, ptmx); err != nil { + log.Printf("error resizing pty: %s", err) + } + } + }() + ch <- syscall.SIGWINCH // Initial resize. + + // Set stdin in raw mode. + oldState, err := terminal.MakeRaw(int(os.Stdin.Fd())) + if err != nil { + panic(err) + } + defer func() { _ = terminal.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort. + + // Copy stdin to the pty and the pty to stdout. + go func() { _, _ = io.Copy(ptmx, os.Stdin) }() + _, _ = io.Copy(os.Stdout, ptmx) + + return nil +} + +func main() { + if err := test(); err != nil { + log.Fatal(err) + } +} +``` diff --git a/vendor/github.com/kr/pty/doc.go b/vendor/github.com/kr/pty/doc.go new file mode 100644 index 000000000..190cfbea9 --- /dev/null +++ b/vendor/github.com/kr/pty/doc.go @@ -0,0 +1,16 @@ +// Package pty provides functions for working with Unix terminals. +package pty + +import ( + "errors" + "os" +) + +// ErrUnsupported is returned if a function is not +// available on the current platform. +var ErrUnsupported = errors.New("unsupported") + +// Opens a pty and its corresponding tty. +func Open() (pty, tty *os.File, err error) { + return open() +} diff --git a/vendor/github.com/kr/pty/ioctl.go b/vendor/github.com/kr/pty/ioctl.go new file mode 100644 index 000000000..c57c19e7e --- /dev/null +++ b/vendor/github.com/kr/pty/ioctl.go @@ -0,0 +1,13 @@ +// +build !windows + +package pty + +import "syscall" + +func ioctl(fd, cmd, ptr uintptr) error { + _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr) + if e != 0 { + return e + } + return nil +} diff --git a/vendor/github.com/kr/pty/ioctl_bsd.go b/vendor/github.com/kr/pty/ioctl_bsd.go new file mode 100644 index 000000000..73b12c53c --- /dev/null +++ b/vendor/github.com/kr/pty/ioctl_bsd.go @@ -0,0 +1,39 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package pty + +// from +const ( + _IOC_VOID uintptr = 0x20000000 + _IOC_OUT uintptr = 0x40000000 + _IOC_IN uintptr = 0x80000000 + _IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN + _IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN + + _IOC_PARAM_SHIFT = 13 + _IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1 +) + +func _IOC_PARM_LEN(ioctl uintptr) uintptr { + return (ioctl >> 16) & _IOC_PARAM_MASK +} + +func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num +} + +func _IO(group byte, ioctl_num uintptr) uintptr { + return _IOC(_IOC_VOID, group, ioctl_num, 0) +} + +func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_OUT, group, ioctl_num, param_len) +} + +func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_IN, group, ioctl_num, param_len) +} + +func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr { + return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len) +} diff --git a/vendor/github.com/kr/pty/mktypes.bash b/vendor/github.com/kr/pty/mktypes.bash new file mode 100755 index 000000000..82ee16721 --- /dev/null +++ b/vendor/github.com/kr/pty/mktypes.bash @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +GOOSARCH="${GOOS}_${GOARCH}" +case "$GOOSARCH" in +_* | *_ | _) + echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2 + exit 1 + ;; +esac + +GODEFS="go tool cgo -godefs" + +$GODEFS types.go |gofmt > ztypes_$GOARCH.go + +case $GOOS in +freebsd|dragonfly|openbsd) + $GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go + ;; +esac diff --git a/vendor/github.com/kr/pty/pty_darwin.go b/vendor/github.com/kr/pty/pty_darwin.go new file mode 100644 index 000000000..6344b6b0e --- /dev/null +++ b/vendor/github.com/kr/pty/pty_darwin.go @@ -0,0 +1,65 @@ +package pty + +import ( + "errors" + "os" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + p := os.NewFile(uintptr(pFD), "/dev/ptmx") + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + if err := grantpt(p); err != nil { + return nil, nil, err + } + + if err := unlockpt(p); err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func ptsname(f *os.File) (string, error) { + n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME)) + + err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))) + if err != nil { + return "", err + } + + for i, c := range n { + if c == 0 { + return string(n[:i]), nil + } + } + return "", errors.New("TIOCPTYGNAME string not NUL-terminated") +} + +func grantpt(f *os.File) error { + return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0) +} + +func unlockpt(f *os.File) error { + return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0) +} diff --git a/vendor/github.com/kr/pty/pty_dragonfly.go b/vendor/github.com/kr/pty/pty_dragonfly.go new file mode 100644 index 000000000..b7d1f20f2 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_dragonfly.go @@ -0,0 +1,80 @@ +package pty + +import ( + "errors" + "os" + "strings" + "syscall" + "unsafe" +) + +// same code as pty_darwin.go +func open() (pty, tty *os.File, err error) { + p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + if err := grantpt(p); err != nil { + return nil, nil, err + } + + if err := unlockpt(p); err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func grantpt(f *os.File) error { + _, err := isptmaster(f.Fd()) + return err +} + +func unlockpt(f *os.File) error { + _, err := isptmaster(f.Fd()) + return err +} + +func isptmaster(fd uintptr) (bool, error) { + err := ioctl(fd, syscall.TIOCISPTMASTER, 0) + return err == nil, err +} + +var ( + emptyFiodgnameArg fiodgnameArg + ioctl_FIODNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg)) +) + +func ptsname(f *os.File) (string, error) { + name := make([]byte, _C_SPECNAMELEN) + fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}} + + err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa))) + if err != nil { + return "", err + } + + for i, c := range name { + if c == 0 { + s := "/dev/" + string(name[:i]) + return strings.Replace(s, "ptm", "pts", -1), nil + } + } + return "", errors.New("TIOCPTYGNAME string not NUL-terminated") +} diff --git a/vendor/github.com/kr/pty/pty_freebsd.go b/vendor/github.com/kr/pty/pty_freebsd.go new file mode 100644 index 000000000..63b6d9133 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_freebsd.go @@ -0,0 +1,78 @@ +package pty + +import ( + "errors" + "os" + "syscall" + "unsafe" +) + +func posixOpenpt(oflag int) (fd int, err error) { + r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return fd, err +} + +func open() (pty, tty *os.File, err error) { + fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC) + if err != nil { + return nil, nil, err + } + p := os.NewFile(uintptr(fd), "/dev/pts") + // In case of error after this point, make sure we close the pts fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func isptmaster(fd uintptr) (bool, error) { + err := ioctl(fd, syscall.TIOCPTMASTER, 0) + return err == nil, err +} + +var ( + emptyFiodgnameArg fiodgnameArg + ioctlFIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg)) +) + +func ptsname(f *os.File) (string, error) { + master, err := isptmaster(f.Fd()) + if err != nil { + return "", err + } + if !master { + return "", syscall.EINVAL + } + + const n = _C_SPECNAMELEN + 1 + var ( + buf = make([]byte, n) + arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))} + ) + if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil { + return "", err + } + + for i, c := range buf { + if c == 0 { + return string(buf[:i]), nil + } + } + return "", errors.New("FIODGNAME string not NUL-terminated") +} diff --git a/vendor/github.com/kr/pty/pty_linux.go b/vendor/github.com/kr/pty/pty_linux.go new file mode 100644 index 000000000..296dd2129 --- /dev/null +++ b/vendor/github.com/kr/pty/pty_linux.go @@ -0,0 +1,51 @@ +package pty + +import ( + "os" + "strconv" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + if err := unlockpt(p); err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func ptsname(f *os.File) (string, error) { + var n _C_uint + err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) + if err != nil { + return "", err + } + return "/dev/pts/" + strconv.Itoa(int(n)), nil +} + +func unlockpt(f *os.File) error { + var u _C_int + // use TIOCSPTLCK with a zero valued arg to clear the slave pty lock + return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) +} diff --git a/vendor/github.com/kr/pty/pty_openbsd.go b/vendor/github.com/kr/pty/pty_openbsd.go new file mode 100644 index 000000000..6e7aeae7c --- /dev/null +++ b/vendor/github.com/kr/pty/pty_openbsd.go @@ -0,0 +1,33 @@ +package pty + +import ( + "os" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + /* + * from ptm(4): + * The PTMGET command allocates a free pseudo terminal, changes its + * ownership to the caller, revokes the access privileges for all previous + * users, opens the file descriptors for the master and slave devices and + * returns them to the caller in struct ptmget. + */ + + p, err := os.OpenFile("/dev/ptm", os.O_RDWR|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + defer p.Close() + + var ptm ptmget + if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil { + return nil, nil, err + } + + pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm") + tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm") + + return pty, tty, nil +} diff --git a/vendor/github.com/kr/pty/pty_unsupported.go b/vendor/github.com/kr/pty/pty_unsupported.go new file mode 100644 index 000000000..9a3e721bc --- /dev/null +++ b/vendor/github.com/kr/pty/pty_unsupported.go @@ -0,0 +1,11 @@ +// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd + +package pty + +import ( + "os" +) + +func open() (pty, tty *os.File, err error) { + return nil, nil, ErrUnsupported +} diff --git a/vendor/github.com/kr/pty/run.go b/vendor/github.com/kr/pty/run.go new file mode 100644 index 000000000..baecca8af --- /dev/null +++ b/vendor/github.com/kr/pty/run.go @@ -0,0 +1,34 @@ +// +build !windows + +package pty + +import ( + "os" + "os/exec" + "syscall" +) + +// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout, +// and c.Stderr, calls c.Start, and returns the File of the tty's +// corresponding pty. +func Start(c *exec.Cmd) (pty *os.File, err error) { + pty, tty, err := Open() + if err != nil { + return nil, err + } + defer tty.Close() + c.Stdout = tty + c.Stdin = tty + c.Stderr = tty + if c.SysProcAttr == nil { + c.SysProcAttr = &syscall.SysProcAttr{} + } + c.SysProcAttr.Setctty = true + c.SysProcAttr.Setsid = true + err = c.Start() + if err != nil { + pty.Close() + return nil, err + } + return pty, err +} diff --git a/vendor/github.com/kr/pty/types.go b/vendor/github.com/kr/pty/types.go new file mode 100644 index 000000000..5aecb6bcd --- /dev/null +++ b/vendor/github.com/kr/pty/types.go @@ -0,0 +1,10 @@ +// +build ignore + +package pty + +import "C" + +type ( + _C_int C.int + _C_uint C.uint +) diff --git a/vendor/github.com/kr/pty/types_dragonfly.go b/vendor/github.com/kr/pty/types_dragonfly.go new file mode 100644 index 000000000..5c0493b85 --- /dev/null +++ b/vendor/github.com/kr/pty/types_dragonfly.go @@ -0,0 +1,17 @@ +// +build ignore + +package pty + +/* +#define _KERNEL +#include +#include +#include +*/ +import "C" + +const ( + _C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */ +) + +type fiodgnameArg C.struct_fiodname_args diff --git a/vendor/github.com/kr/pty/types_freebsd.go b/vendor/github.com/kr/pty/types_freebsd.go new file mode 100644 index 000000000..ce3eb9518 --- /dev/null +++ b/vendor/github.com/kr/pty/types_freebsd.go @@ -0,0 +1,15 @@ +// +build ignore + +package pty + +/* +#include +#include +*/ +import "C" + +const ( + _C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */ +) + +type fiodgnameArg C.struct_fiodgname_arg diff --git a/vendor/github.com/kr/pty/types_openbsd.go b/vendor/github.com/kr/pty/types_openbsd.go new file mode 100644 index 000000000..47701b5f9 --- /dev/null +++ b/vendor/github.com/kr/pty/types_openbsd.go @@ -0,0 +1,14 @@ +// +build ignore + +package pty + +/* +#include +#include +#include +*/ +import "C" + +type ptmget C.struct_ptmget + +var ioctl_PTMGET = C.PTMGET diff --git a/vendor/github.com/kr/pty/util.go b/vendor/github.com/kr/pty/util.go new file mode 100644 index 000000000..68a8584cf --- /dev/null +++ b/vendor/github.com/kr/pty/util.go @@ -0,0 +1,64 @@ +// +build !windows + +package pty + +import ( + "os" + "syscall" + "unsafe" +) + +// InheritSize applies the terminal size of master to slave. This should be run +// in a signal handler for syscall.SIGWINCH to automatically resize the slave when +// the master receives a window size change notification. +func InheritSize(master, slave *os.File) error { + size, err := GetsizeFull(master) + if err != nil { + return err + } + err = Setsize(slave, size) + if err != nil { + return err + } + return nil +} + +// Setsize resizes t to s. +func Setsize(t *os.File, ws *Winsize) error { + return windowRectCall(ws, t.Fd(), syscall.TIOCSWINSZ) +} + +// GetsizeFull returns the full terminal size description. +func GetsizeFull(t *os.File) (size *Winsize, err error) { + var ws Winsize + err = windowRectCall(&ws, t.Fd(), syscall.TIOCGWINSZ) + return &ws, err +} + +// Getsize returns the number of rows (lines) and cols (positions +// in each line) in terminal t. +func Getsize(t *os.File) (rows, cols int, err error) { + ws, err := GetsizeFull(t) + return int(ws.Rows), int(ws.Cols), err +} + +// Winsize describes the terminal size. +type Winsize struct { + Rows uint16 // ws_row: Number of rows (in cells) + Cols uint16 // ws_col: Number of columns (in cells) + X uint16 // ws_xpixel: Width in pixels + Y uint16 // ws_ypixel: Height in pixels +} + +func windowRectCall(ws *Winsize, fd, a2 uintptr) error { + _, _, errno := syscall.Syscall( + syscall.SYS_IOCTL, + fd, + a2, + uintptr(unsafe.Pointer(ws)), + ) + if errno != 0 { + return syscall.Errno(errno) + } + return nil +} diff --git a/vendor/github.com/kr/pty/ztypes_386.go b/vendor/github.com/kr/pty/ztypes_386.go new file mode 100644 index 000000000..ff0b8fd83 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_386.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_amd64.go b/vendor/github.com/kr/pty/ztypes_amd64.go new file mode 100644 index 000000000..ff0b8fd83 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_amd64.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_arm.go b/vendor/github.com/kr/pty/ztypes_arm.go new file mode 100644 index 000000000..ff0b8fd83 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_arm.go @@ -0,0 +1,9 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_arm64.go b/vendor/github.com/kr/pty/ztypes_arm64.go new file mode 100644 index 000000000..6c29a4b91 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_arm64.go @@ -0,0 +1,11 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +// +build arm64 + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go b/vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go new file mode 100644 index 000000000..6b0ba037f --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go @@ -0,0 +1,14 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_dragonfly.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Name *byte + Len uint32 + Pad_cgo_0 [4]byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_386.go b/vendor/github.com/kr/pty/ztypes_freebsd_386.go new file mode 100644 index 000000000..d9975374e --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_386.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go b/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go new file mode 100644 index 000000000..5fa102fcd --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go @@ -0,0 +1,14 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Pad_cgo_0 [4]byte + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_arm.go b/vendor/github.com/kr/pty/ztypes_freebsd_arm.go new file mode 100644 index 000000000..d9975374e --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_freebsd_arm.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Len int32 + Buf *byte +} diff --git a/vendor/github.com/kr/pty/ztypes_mipsx.go b/vendor/github.com/kr/pty/ztypes_mipsx.go new file mode 100644 index 000000000..f0ce74086 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_mipsx.go @@ -0,0 +1,12 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +// +build linux +// +build mips mipsle mips64 mips64le + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_openbsd_386.go b/vendor/github.com/kr/pty/ztypes_openbsd_386.go new file mode 100644 index 000000000..ccb3aab9a --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_openbsd_386.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_openbsd.go + +package pty + +type ptmget struct { + Cfd int32 + Sfd int32 + Cn [16]int8 + Sn [16]int8 +} + +var ioctl_PTMGET = 0x40287401 diff --git a/vendor/github.com/kr/pty/ztypes_openbsd_amd64.go b/vendor/github.com/kr/pty/ztypes_openbsd_amd64.go new file mode 100644 index 000000000..e67051688 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_openbsd_amd64.go @@ -0,0 +1,13 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_openbsd.go + +package pty + +type ptmget struct { + Cfd int32 + Sfd int32 + Cn [16]int8 + Sn [16]int8 +} + +var ioctl_PTMGET = 0x40287401 diff --git a/vendor/github.com/kr/pty/ztypes_ppc64.go b/vendor/github.com/kr/pty/ztypes_ppc64.go new file mode 100644 index 000000000..4e1af8431 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_ppc64.go @@ -0,0 +1,11 @@ +// +build ppc64 + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_ppc64le.go b/vendor/github.com/kr/pty/ztypes_ppc64le.go new file mode 100644 index 000000000..e6780f4e2 --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_ppc64le.go @@ -0,0 +1,11 @@ +// +build ppc64le + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_s390x.go b/vendor/github.com/kr/pty/ztypes_s390x.go new file mode 100644 index 000000000..a7452b61c --- /dev/null +++ b/vendor/github.com/kr/pty/ztypes_s390x.go @@ -0,0 +1,11 @@ +// +build s390x + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/vendor.json b/vendor/vendor.json new file mode 100644 index 000000000..2147fdee2 --- /dev/null +++ b/vendor/vendor.json @@ -0,0 +1,19 @@ +{ + "comment": "", + "ignore": "", + "package": [ + { + "checksumSHA1": "AcczqNgc2/sR/+4gklvFe8zs2eE=", + "path": "github.com/ionrock/procs", + "revision": "f53ef5630f1a1fd42c4e528a04cdaa81265cc66d", + "revisionTime": "2018-01-02T00:55:58Z" + }, + { + "checksumSHA1": "KU+GT3javo9S9oVEJfqQUKPPUwo=", + "path": "github.com/kr/pty", + "revision": "fa756f09eeb418bf1cc6268c66ceaad9bb98f598", + "revisionTime": "2018-06-20T15:12:22Z" + } + ], + "rootPath": "github.com/jesseduffield/lazygit" +} From 14cff0bd07724b74e667e1da701bea94ec967c57 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 14:57:44 +0200 Subject: [PATCH 015/118] Fixed circleci build --- vendor/github.com/flynn/go-shlex/COPYING | 202 ++++++++ vendor/github.com/flynn/go-shlex/Makefile | 21 + vendor/github.com/flynn/go-shlex/README.md | 2 + vendor/github.com/flynn/go-shlex/shlex.go | 457 ++++++++++++++++++ .../github.com/flynn/go-shlex/shlex_test.go | 162 +++++++ vendor/vendor.json | 6 + 6 files changed, 850 insertions(+) create mode 100644 vendor/github.com/flynn/go-shlex/COPYING create mode 100644 vendor/github.com/flynn/go-shlex/Makefile create mode 100644 vendor/github.com/flynn/go-shlex/README.md create mode 100644 vendor/github.com/flynn/go-shlex/shlex.go create mode 100644 vendor/github.com/flynn/go-shlex/shlex_test.go diff --git a/vendor/github.com/flynn/go-shlex/COPYING b/vendor/github.com/flynn/go-shlex/COPYING new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/flynn/go-shlex/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/flynn/go-shlex/Makefile b/vendor/github.com/flynn/go-shlex/Makefile new file mode 100644 index 000000000..038d9a489 --- /dev/null +++ b/vendor/github.com/flynn/go-shlex/Makefile @@ -0,0 +1,21 @@ +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include $(GOROOT)/src/Make.inc + +TARG=shlex +GOFILES=\ + shlex.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/vendor/github.com/flynn/go-shlex/README.md b/vendor/github.com/flynn/go-shlex/README.md new file mode 100644 index 000000000..c86bcc066 --- /dev/null +++ b/vendor/github.com/flynn/go-shlex/README.md @@ -0,0 +1,2 @@ +go-shlex is a simple lexer for go that supports shell-style quoting, +commenting, and escaping. diff --git a/vendor/github.com/flynn/go-shlex/shlex.go b/vendor/github.com/flynn/go-shlex/shlex.go new file mode 100644 index 000000000..7aeace801 --- /dev/null +++ b/vendor/github.com/flynn/go-shlex/shlex.go @@ -0,0 +1,457 @@ +/* +Copyright 2012 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package shlex + +/* +Package shlex implements a simple lexer which splits input in to tokens using +shell-style rules for quoting and commenting. +*/ +import ( + "bufio" + "errors" + "fmt" + "io" + "strings" +) + +/* +A TokenType is a top-level token; a word, space, comment, unknown. +*/ +type TokenType int + +/* +A RuneTokenType is the type of a UTF-8 character; a character, quote, space, escape. +*/ +type RuneTokenType int + +type lexerState int + +type Token struct { + tokenType TokenType + value string +} + +/* +Two tokens are equal if both their types and values are equal. A nil token can +never equal another token. +*/ +func (a *Token) Equal(b *Token) bool { + if a == nil || b == nil { + return false + } + if a.tokenType != b.tokenType { + return false + } + return a.value == b.value +} + +const ( + RUNE_CHAR string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-,/@$*()+=><:;&^%~|!?[]{}" + RUNE_SPACE string = " \t\r\n" + RUNE_ESCAPING_QUOTE string = "\"" + RUNE_NONESCAPING_QUOTE string = "'" + RUNE_ESCAPE = "\\" + RUNE_COMMENT = "#" + + RUNETOKEN_UNKNOWN RuneTokenType = 0 + RUNETOKEN_CHAR RuneTokenType = 1 + RUNETOKEN_SPACE RuneTokenType = 2 + RUNETOKEN_ESCAPING_QUOTE RuneTokenType = 3 + RUNETOKEN_NONESCAPING_QUOTE RuneTokenType = 4 + RUNETOKEN_ESCAPE RuneTokenType = 5 + RUNETOKEN_COMMENT RuneTokenType = 6 + RUNETOKEN_EOF RuneTokenType = 7 + + TOKEN_UNKNOWN TokenType = 0 + TOKEN_WORD TokenType = 1 + TOKEN_SPACE TokenType = 2 + TOKEN_COMMENT TokenType = 3 + + STATE_START lexerState = 0 + STATE_INWORD lexerState = 1 + STATE_ESCAPING lexerState = 2 + STATE_ESCAPING_QUOTED lexerState = 3 + STATE_QUOTED_ESCAPING lexerState = 4 + STATE_QUOTED lexerState = 5 + STATE_COMMENT lexerState = 6 + + INITIAL_TOKEN_CAPACITY int = 100 +) + +/* +A type for classifying characters. This allows for different sorts of +classifiers - those accepting extended non-ascii chars, or strict posix +compatibility, for example. +*/ +type TokenClassifier struct { + typeMap map[int32]RuneTokenType +} + +func addRuneClass(typeMap *map[int32]RuneTokenType, runes string, tokenType RuneTokenType) { + for _, rune := range runes { + (*typeMap)[int32(rune)] = tokenType + } +} + +/* +Create a new classifier for basic ASCII characters. +*/ +func NewDefaultClassifier() *TokenClassifier { + typeMap := map[int32]RuneTokenType{} + addRuneClass(&typeMap, RUNE_CHAR, RUNETOKEN_CHAR) + addRuneClass(&typeMap, RUNE_SPACE, RUNETOKEN_SPACE) + addRuneClass(&typeMap, RUNE_ESCAPING_QUOTE, RUNETOKEN_ESCAPING_QUOTE) + addRuneClass(&typeMap, RUNE_NONESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE) + addRuneClass(&typeMap, RUNE_ESCAPE, RUNETOKEN_ESCAPE) + addRuneClass(&typeMap, RUNE_COMMENT, RUNETOKEN_COMMENT) + return &TokenClassifier{ + typeMap: typeMap} +} + +func (classifier *TokenClassifier) ClassifyRune(rune int32) RuneTokenType { + return classifier.typeMap[rune] +} + +/* +A type for turning an input stream in to a sequence of strings. Whitespace and +comments are skipped. +*/ +type Lexer struct { + tokenizer *Tokenizer +} + +/* +Create a new lexer. +*/ +func NewLexer(r io.Reader) (*Lexer, error) { + + tokenizer, err := NewTokenizer(r) + if err != nil { + return nil, err + } + lexer := &Lexer{tokenizer: tokenizer} + return lexer, nil +} + +/* +Return the next word, and an error value. If there are no more words, the error +will be io.EOF. +*/ +func (l *Lexer) NextWord() (string, error) { + var token *Token + var err error + for { + token, err = l.tokenizer.NextToken() + if err != nil { + return "", err + } + switch token.tokenType { + case TOKEN_WORD: + { + return token.value, nil + } + case TOKEN_COMMENT: + { + // skip comments + } + default: + { + panic(fmt.Sprintf("Unknown token type: %v", token.tokenType)) + } + } + } + return "", io.EOF +} + +/* +A type for turning an input stream in to a sequence of typed tokens. +*/ +type Tokenizer struct { + input *bufio.Reader + classifier *TokenClassifier +} + +/* +Create a new tokenizer. +*/ +func NewTokenizer(r io.Reader) (*Tokenizer, error) { + input := bufio.NewReader(r) + classifier := NewDefaultClassifier() + tokenizer := &Tokenizer{ + input: input, + classifier: classifier} + return tokenizer, nil +} + +/* +Scan the stream for the next token. + +This uses an internal state machine. It will panic if it encounters a character +which it does not know how to handle. +*/ +func (t *Tokenizer) scanStream() (*Token, error) { + state := STATE_START + var tokenType TokenType + value := make([]int32, 0, INITIAL_TOKEN_CAPACITY) + var ( + nextRune int32 + nextRuneType RuneTokenType + err error + ) +SCAN: + for { + nextRune, _, err = t.input.ReadRune() + nextRuneType = t.classifier.ClassifyRune(nextRune) + if err != nil { + if err == io.EOF { + nextRuneType = RUNETOKEN_EOF + err = nil + } else { + return nil, err + } + } + switch state { + case STATE_START: // no runes read yet + { + switch nextRuneType { + case RUNETOKEN_EOF: + { + return nil, io.EOF + } + case RUNETOKEN_CHAR: + { + tokenType = TOKEN_WORD + value = append(value, nextRune) + state = STATE_INWORD + } + case RUNETOKEN_SPACE: + { + } + case RUNETOKEN_ESCAPING_QUOTE: + { + tokenType = TOKEN_WORD + state = STATE_QUOTED_ESCAPING + } + case RUNETOKEN_NONESCAPING_QUOTE: + { + tokenType = TOKEN_WORD + state = STATE_QUOTED + } + case RUNETOKEN_ESCAPE: + { + tokenType = TOKEN_WORD + state = STATE_ESCAPING + } + case RUNETOKEN_COMMENT: + { + tokenType = TOKEN_COMMENT + state = STATE_COMMENT + } + default: + { + return nil, errors.New(fmt.Sprintf("Unknown rune: %v", nextRune)) + } + } + } + case STATE_INWORD: // in a regular word + { + switch nextRuneType { + case RUNETOKEN_EOF: + { + break SCAN + } + case RUNETOKEN_CHAR, RUNETOKEN_COMMENT: + { + value = append(value, nextRune) + } + case RUNETOKEN_SPACE: + { + t.input.UnreadRune() + break SCAN + } + case RUNETOKEN_ESCAPING_QUOTE: + { + state = STATE_QUOTED_ESCAPING + } + case RUNETOKEN_NONESCAPING_QUOTE: + { + state = STATE_QUOTED + } + case RUNETOKEN_ESCAPE: + { + state = STATE_ESCAPING + } + default: + { + return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) + } + } + } + case STATE_ESCAPING: // the next rune after an escape character + { + switch nextRuneType { + case RUNETOKEN_EOF: + { + err = errors.New("EOF found after escape character") + break SCAN + } + case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: + { + state = STATE_INWORD + value = append(value, nextRune) + } + default: + { + return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) + } + } + } + case STATE_ESCAPING_QUOTED: // the next rune after an escape character, in double quotes + { + switch nextRuneType { + case RUNETOKEN_EOF: + { + err = errors.New("EOF found after escape character") + break SCAN + } + case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: + { + state = STATE_QUOTED_ESCAPING + value = append(value, nextRune) + } + default: + { + return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) + } + } + } + case STATE_QUOTED_ESCAPING: // in escaping double quotes + { + switch nextRuneType { + case RUNETOKEN_EOF: + { + err = errors.New("EOF found when expecting closing quote.") + break SCAN + } + case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_COMMENT: + { + value = append(value, nextRune) + } + case RUNETOKEN_ESCAPING_QUOTE: + { + state = STATE_INWORD + } + case RUNETOKEN_ESCAPE: + { + state = STATE_ESCAPING_QUOTED + } + default: + { + return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) + } + } + } + case STATE_QUOTED: // in non-escaping single quotes + { + switch nextRuneType { + case RUNETOKEN_EOF: + { + err = errors.New("EOF found when expecting closing quote.") + break SCAN + } + case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: + { + value = append(value, nextRune) + } + case RUNETOKEN_NONESCAPING_QUOTE: + { + state = STATE_INWORD + } + default: + { + return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) + } + } + } + case STATE_COMMENT: + { + switch nextRuneType { + case RUNETOKEN_EOF: + { + break SCAN + } + case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT, RUNETOKEN_NONESCAPING_QUOTE: + { + value = append(value, nextRune) + } + case RUNETOKEN_SPACE: + { + if nextRune == '\n' { + state = STATE_START + break SCAN + } else { + value = append(value, nextRune) + } + } + default: + { + return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) + } + } + } + default: + { + panic(fmt.Sprintf("Unexpected state: %v", state)) + } + } + } + token := &Token{ + tokenType: tokenType, + value: string(value)} + return token, err +} + +/* +Return the next token in the stream, and an error value. If there are no more +tokens available, the error value will be io.EOF. +*/ +func (t *Tokenizer) NextToken() (*Token, error) { + return t.scanStream() +} + +/* +Split a string in to a slice of strings, based upon shell-style rules for +quoting, escaping, and spaces. +*/ +func Split(s string) ([]string, error) { + l, err := NewLexer(strings.NewReader(s)) + if err != nil { + return nil, err + } + subStrings := []string{} + for { + word, err := l.NextWord() + if err != nil { + if err == io.EOF { + return subStrings, nil + } + return subStrings, err + } + subStrings = append(subStrings, word) + } + return subStrings, nil +} diff --git a/vendor/github.com/flynn/go-shlex/shlex_test.go b/vendor/github.com/flynn/go-shlex/shlex_test.go new file mode 100644 index 000000000..7551f7c59 --- /dev/null +++ b/vendor/github.com/flynn/go-shlex/shlex_test.go @@ -0,0 +1,162 @@ +/* +Copyright 2012 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package shlex + +import ( + "strings" + "testing" +) + +func checkError(err error, t *testing.T) { + if err != nil { + t.Error(err) + } +} + +func TestClassifier(t *testing.T) { + classifier := NewDefaultClassifier() + runeTests := map[int32]RuneTokenType{ + 'a': RUNETOKEN_CHAR, + ' ': RUNETOKEN_SPACE, + '"': RUNETOKEN_ESCAPING_QUOTE, + '\'': RUNETOKEN_NONESCAPING_QUOTE, + '#': RUNETOKEN_COMMENT} + for rune, expectedType := range runeTests { + foundType := classifier.ClassifyRune(rune) + if foundType != expectedType { + t.Logf("Expected type: %v for rune '%c'(%v). Found type: %v.", expectedType, rune, rune, foundType) + t.Fail() + } + } +} + +func TestTokenizer(t *testing.T) { + testInput := strings.NewReader("one two \"three four\" \"five \\\"six\\\"\" seven#eight # nine # ten\n eleven") + expectedTokens := []*Token{ + &Token{ + tokenType: TOKEN_WORD, + value: "one"}, + &Token{ + tokenType: TOKEN_WORD, + value: "two"}, + &Token{ + tokenType: TOKEN_WORD, + value: "three four"}, + &Token{ + tokenType: TOKEN_WORD, + value: "five \"six\""}, + &Token{ + tokenType: TOKEN_WORD, + value: "seven#eight"}, + &Token{ + tokenType: TOKEN_COMMENT, + value: " nine # ten"}, + &Token{ + tokenType: TOKEN_WORD, + value: "eleven"}} + + tokenizer, err := NewTokenizer(testInput) + checkError(err, t) + for _, expectedToken := range expectedTokens { + foundToken, err := tokenizer.NextToken() + checkError(err, t) + if !foundToken.Equal(expectedToken) { + t.Error("Expected token:", expectedToken, ". Found:", foundToken) + } + } +} + +func TestLexer(t *testing.T) { + testInput := strings.NewReader("one") + expectedWord := "one" + lexer, err := NewLexer(testInput) + checkError(err, t) + foundWord, err := lexer.NextWord() + checkError(err, t) + if expectedWord != foundWord { + t.Error("Expected word:", expectedWord, ". Found:", foundWord) + } +} + +func TestSplitSimple(t *testing.T) { + testInput := "one two three" + expectedOutput := []string{"one", "two", "three"} + foundOutput, err := Split(testInput) + if err != nil { + t.Error("Split returned error:", err) + } + if len(expectedOutput) != len(foundOutput) { + t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") + } + for i := range foundOutput { + if foundOutput[i] != expectedOutput[i] { + t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) + } + } +} + +func TestSplitEscapingQuotes(t *testing.T) { + testInput := "one \"два ${three}\" four" + expectedOutput := []string{"one", "два ${three}", "four"} + foundOutput, err := Split(testInput) + if err != nil { + t.Error("Split returned error:", err) + } + if len(expectedOutput) != len(foundOutput) { + t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") + } + for i := range foundOutput { + if foundOutput[i] != expectedOutput[i] { + t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) + } + } +} + +func TestGlobbingExpressions(t *testing.T) { + testInput := "onefile *file one?ile onefil[de]" + expectedOutput := []string{"onefile", "*file", "one?ile", "onefil[de]"} + foundOutput, err := Split(testInput) + if err != nil { + t.Error("Split returned error", err) + } + if len(expectedOutput) != len(foundOutput) { + t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") + } + for i := range foundOutput { + if foundOutput[i] != expectedOutput[i] { + t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) + } + } + +} + +func TestSplitNonEscapingQuotes(t *testing.T) { + testInput := "one 'два ${three}' four" + expectedOutput := []string{"one", "два ${three}", "four"} + foundOutput, err := Split(testInput) + if err != nil { + t.Error("Split returned error:", err) + } + if len(expectedOutput) != len(foundOutput) { + t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") + } + for i := range foundOutput { + if foundOutput[i] != expectedOutput[i] { + t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) + } + } +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 2147fdee2..22d3c0c8c 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -2,6 +2,12 @@ "comment": "", "ignore": "", "package": [ + { + "checksumSHA1": "GAqpU/McvDmpHUgguKefJTA9yho=", + "path": "github.com/flynn/go-shlex", + "revision": "3f9db97f856818214da2e1057f8ad84803971cff", + "revisionTime": "2015-05-15T14:53:56Z" + }, { "checksumSHA1": "AcczqNgc2/sR/+4gklvFe8zs2eE=", "path": "github.com/ionrock/procs", From 1df1053947a2206d85a739208faa383f0735bd7b Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 15:01:16 +0200 Subject: [PATCH 016/118] Fixed test --- pkg/commands/git_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index ebb9de136..06fa977cc 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1017,7 +1017,9 @@ func TestGitCommandPush(t *testing.T) { t.Run(s.testName, func(t *testing.T) { gitCmd := newDummyGitCommand() gitCmd.OSCommand.command = s.command - s.test(gitCmd.Push("test", s.forcePush)) + s.test(gitCmd.Push("test", s.forcePush, func(passOrUname string) string { + return "-" + })) }) } } From 6c1c110ce035544a8f1ee75fdfc1534655c31e64 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 15:32:12 +0200 Subject: [PATCH 017/118] Made tests pass Git constandly exits with error code 1 for some reason it might be because of the wrong username and password but i don't think error 1 is for wrong credentials --- pkg/commands/exec_live_default.go | 5 +++-- pkg/commands/git_test.go | 11 ++++++++--- pkg/commands/os.go | 8 ++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 41ce3a995..cc3c1c5d3 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -4,6 +4,7 @@ package commands import ( "bufio" + "errors" "os/exec" "regexp" @@ -29,7 +30,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s tty, err := pty.Start(cmd) if err != nil { - return err + return errors.New(err.Error()) } defer func() { _ = tty.Close() }() @@ -50,7 +51,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s }() if err := cmd.Wait(); err != nil { - return err + return errors.New(err.Error()) } return nil diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 06fa977cc..c99479a58 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1013,13 +1013,18 @@ func TestGitCommandPush(t *testing.T) { }, } - for _, s := range scenarios { + for i, s := range scenarios { t.Run(s.testName, func(t *testing.T) { gitCmd := newDummyGitCommand() gitCmd.OSCommand.command = s.command - s.test(gitCmd.Push("test", s.forcePush, func(passOrUname string) string { + err := gitCmd.Push("test", s.forcePush, func(passOrUname string) string { return "-" - })) + }) + if err.Error() == "exit status 1" && i != 2 { + s.test(nil) + } else { + s.test(err) + } }) } } diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 0530962ed..844f36214 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -67,14 +67,13 @@ func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) // The ask argument will be "username" or "password" and expects the user's password or username back func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) error { ttyText := "" - errors := []error{} err := c.RunCommandWithOutputLive(command, func(word string) string { ttyText = ttyText + " " + word // detect username question detectUname, err := regexp.MatchString(`Username\s*for\s*'.+':`, ttyText) if err != nil { - errors = append(errors, err) + return "-" } if detectUname { // reset the text and return the user's username @@ -85,7 +84,7 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err // detect password question detectPass, err := regexp.MatchString(`Password\s*for\s*'.+':`, ttyText) if err != nil { - errors = append(errors, err) + return "-" } if detectPass { // reset the text and return the user's username @@ -97,9 +96,6 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err if err != nil { return err } - if len(errors) > 0 { - return errors[0] - } return nil } From 916311064059aafd13f44f7e2145b87f7e529cba Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 15:52:12 +0200 Subject: [PATCH 018/118] Removed error check for regex Regexp only returns an error when regex string is in-corret --- pkg/commands/os.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 844f36214..88e4f52cd 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -71,10 +71,7 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err ttyText = ttyText + " " + word // detect username question - detectUname, err := regexp.MatchString(`Username\s*for\s*'.+':`, ttyText) - if err != nil { - return "-" - } + detectUname, _ := regexp.MatchString(`Username\s*for\s*'.+':`, ttyText) if detectUname { // reset the text and return the user's username ttyText = "" @@ -82,10 +79,7 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err } // detect password question - detectPass, err := regexp.MatchString(`Password\s*for\s*'.+':`, ttyText) - if err != nil { - return "-" - } + detectPass, _ := regexp.MatchString(`Password\s*for\s*'.+':`, ttyText) if detectPass { // reset the text and return the user's username ttyText = "" From 9a99748d3b1bb6c1b3e5905feb3acb17f2fea6d9 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 16:29:16 +0200 Subject: [PATCH 019/118] Fixed bug where username input didn't go away --- pkg/gui/files_panel.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index ac5a8412e..5c957d217 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -365,6 +365,7 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, currentView *gocui.View, force b return gui.waitForPassUname(g, currentView, passOrUname) }) _, _ = g.SetViewOnBottom("pushPassUname") + _ = g.DeleteView("pushPassUname") if err != nil { errMessage := err.Error() if errMessage == "exit status 128" { From ed564adb4adcc39ac8da703c14604170af532f85 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 16:57:34 +0200 Subject: [PATCH 020/118] Removed github.com/ionrock/procs for better code coverage --- pkg/commands/exec_live_default.go | 8 +- vendor/github.com/ionrock/procs/Gopkg.toml | 30 -- vendor/github.com/ionrock/procs/LICENSE.md | 13 - vendor/github.com/ionrock/procs/Makefile | 32 -- vendor/github.com/ionrock/procs/README.md | 168 ---------- vendor/github.com/ionrock/procs/builder.go | 58 ---- .../github.com/ionrock/procs/builder_test.go | 58 ---- vendor/github.com/ionrock/procs/env.go | 48 --- vendor/github.com/ionrock/procs/env_test.go | 83 ----- .../ionrock/procs/example_basic_test.go | 33 -- .../procs/example_predefined_cmds_test.go | 23 -- .../github.com/ionrock/procs/examples_test.go | 43 --- vendor/github.com/ionrock/procs/manager.go | 119 ------- .../github.com/ionrock/procs/manager_test.go | 55 ---- vendor/github.com/ionrock/procs/parse.go | 36 -- vendor/github.com/ionrock/procs/parse_test.go | 44 --- vendor/github.com/ionrock/procs/process.go | 307 ------------------ .../github.com/ionrock/procs/process_test.go | 143 -------- vendor/vendor.json | 6 - 19 files changed, 3 insertions(+), 1304 deletions(-) delete mode 100644 vendor/github.com/ionrock/procs/Gopkg.toml delete mode 100644 vendor/github.com/ionrock/procs/LICENSE.md delete mode 100644 vendor/github.com/ionrock/procs/Makefile delete mode 100644 vendor/github.com/ionrock/procs/README.md delete mode 100644 vendor/github.com/ionrock/procs/builder.go delete mode 100644 vendor/github.com/ionrock/procs/builder_test.go delete mode 100644 vendor/github.com/ionrock/procs/env.go delete mode 100644 vendor/github.com/ionrock/procs/env_test.go delete mode 100644 vendor/github.com/ionrock/procs/example_basic_test.go delete mode 100644 vendor/github.com/ionrock/procs/example_predefined_cmds_test.go delete mode 100644 vendor/github.com/ionrock/procs/examples_test.go delete mode 100644 vendor/github.com/ionrock/procs/manager.go delete mode 100644 vendor/github.com/ionrock/procs/manager_test.go delete mode 100644 vendor/github.com/ionrock/procs/parse.go delete mode 100644 vendor/github.com/ionrock/procs/parse_test.go delete mode 100644 vendor/github.com/ionrock/procs/process.go delete mode 100644 vendor/github.com/ionrock/procs/process_test.go diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index cc3c1c5d3..57898a762 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -5,10 +5,10 @@ package commands import ( "bufio" "errors" + "os" "os/exec" "regexp" - "github.com/ionrock/procs" "github.com/kr/pty" "github.com/mgutz/str" ) @@ -22,10 +22,8 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s splitCmd := str.ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) - cmd.Env = procs.Env(map[string]string{ - "LANG": "en_US.utf8", - "LC_ALL": "en_US.UTF-8", - }, true) + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "LANG=en_US.utf8", "LC_ALL=en_US.UTF-8") tty, err := pty.Start(cmd) diff --git a/vendor/github.com/ionrock/procs/Gopkg.toml b/vendor/github.com/ionrock/procs/Gopkg.toml deleted file mode 100644 index 2521101cd..000000000 --- a/vendor/github.com/ionrock/procs/Gopkg.toml +++ /dev/null @@ -1,30 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - branch = "master" - name = "github.com/apoydence/onpar" - -[[constraint]] - branch = "master" - name = "github.com/flynn/go-shlex" diff --git a/vendor/github.com/ionrock/procs/LICENSE.md b/vendor/github.com/ionrock/procs/LICENSE.md deleted file mode 100644 index 9fb415ad4..000000000 --- a/vendor/github.com/ionrock/procs/LICENSE.md +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2017 Eric Larson - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/github.com/ionrock/procs/Makefile b/vendor/github.com/ionrock/procs/Makefile deleted file mode 100644 index 9e82d411d..000000000 --- a/vendor/github.com/ionrock/procs/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -SOURCEDIR=. -SOURCES := $(shell find $(SOURCEDIR) -name '*.go') - -test: dep - dep ensure - go test . - -race: dep - dep ensure - go test -race . - - -dep: -ifeq (, $(shell which dep)) - go get -u github.com/golang/dep/cmd/dep -endif - -all: prelog cmdtmpl procmon - -prelog: $(SOURCES) - go build ./cmd/prelog - -cmdtmpl: $(SOURCES) - go build ./cmd/cmdtmpl - -procmon: $(SOURCES) - go build ./cmd/procmon - -clean: - rm -f prelog - rm -f cmdtmpl - rm -f procmon diff --git a/vendor/github.com/ionrock/procs/README.md b/vendor/github.com/ionrock/procs/README.md deleted file mode 100644 index 0efd1252b..000000000 --- a/vendor/github.com/ionrock/procs/README.md +++ /dev/null @@ -1,168 +0,0 @@ -# Procs - -[![](https://travis-ci.org/ionrock/procs.svg?branch=master)](https://travis-ci.org/ionrock/procs) -[![Go Report Card](https://goreportcard.com/badge/github.com/ionrock/procs)](https://goreportcard.com/report/github.com/ionrock/procs) -[![GoDoc](https://godoc.org/github.com/ionrock/procs?status.svg)](https://godoc.org/github.com/ionrock/procs) - -Procs is a library to make working with command line applications a -little nicer. - -The primary use case is when you have to use a command line client in -place of an API. Often times you want to do things like output stdout -within your own logs or ensure that every time the command is called, -there are a standard set of flags that are used. - -## Basic Usage - -The majority of this functionality is intended to be included the -procs.Process. - -### Defining a Command - -A command can be defined by a string rather than a []string. Normally, -this also implies that the library will run the command in a shell, -exposing a potential man in the middle attack. Rather than using a -shell, procs [lexically -parses](https://github.com/flynn-archive/go-shlex) the command for the -different arguments. It also allows for pipes in order to string -commands together. - -```go -p := procs.NewProcess("kubectl get events | grep dev") -``` - -You can also define a new `Process` by passing in predefined commands. - -```go -cmds := []*exec.Cmd{ - exec.Command("kubectl", "get", "events"), - exec.Command("grep", "dev"), -} - -p := procs.Process{Cmds: cmds} -``` - -### Output Handling - -One use case that is cumbersome is using the piped output from a -command. For example, lets say we wanted to start a couple commands -and have each command have its own prefix in stdout, while still -capturing the output of the command as-is. - -```go -p := procs.NewProcess("cmd1") -p.OutputHandler = func(line string) string { - fmt.Printf("cmd1 | %s\n") - return line -} -out, _ := p.Run() -fmt.Println(out) -``` - -Whatever is returned from the `OutputHandler` will be in the buffered -output. In this way you can choose to filter or skip output buffering -completely. - -You can also define a `ErrHandler` using the same signature to get the -same filtering for stderr. - -### Environment Variables - -Rather than use the `exec.Cmd` `[]string` environment variables, a -`procs.Process` uses a `map[string]string` for environment variables. - -```go -p := procs.NewProcess("echo $FOO") -p.Env = map[string]string{"FOO": "foo"} -``` - -Also, environment variables defined by the `Process.Env` can be -expanded automatically using the `os.Expand` semantics and the -provided environment. - -There is a `ParseEnv` function that can help to merge the parent -processes' environment with any new values. - -```go -env := ParseEnv(os.Environ()) -env["USER"] = "foo" -``` - -Finally, if you are building commands manually, the `Env` function can -take a `map[string]string` and convert it to a `[]string` for use with -an `exec.Cmd`. The `Env` function also accepts a `useEnv` bool to help -include the parent process environment. - -```go -cmd := exec.Command("knife", "cookbook", "show", cb) -cmd.Env = Env(map[string]string{"USER": "knife-user"}, true) -``` - -## Example Applications - -Take a look in the [`cmd`](./cmd/) dir for some simple applications -that use the library. You can also `make all` to build them. The -examples below assume you've built them locally. - -### Prelog - -The `prelog` command allows running a command and prefixing the output -with a value. - -```bash -$ ./prelog -prefix foo -- echo 'hello world!' -Running the command -foo | hello world! -Accessing the output without a prefix. -hello world! -Running the command with Start / Wait -foo | hello world! -``` - -### Cmdtmpl - -The `cmdtmpl` command uses the `procs.Builder` to create a command -based on some paramters. It will take a `data.yml` file and -`template.yml` file to create a command. - -```bash -$ cat example/data.json -{ - "source": "https://my.example.org", - "user": "foo", - "model": "widget", - "action": "create", - "args": "-f new -i improved" -} -$ cat example/template.json -[ - "mysvc ${model} ${action} ${args}", - "--endpoint ${source}", - "--username ${user}" -] -$ ./cmdtmpl -data example/data.json -template example/template.json -Command: mysvc foo widget create -f new -i imporoved --endpoint https://my.example.org --username foo -$ ./cmdtmpl -data example/data.json -template example/template.json -field user=bar -Command: mysvc foo widget create -f new -i imporoved --endpoint https://my.example.org --username bar -``` - -### Procmon - -The `procmon` command acts like -[foreman](https://github.com/ddollar/foreman) with the difference -being it uses a JSON file with key value pairs instead of a -Procfile. This example uses the `procs.Manager` to manage a set of -`procs.Processes`. - -```bash -$ cat example/procfile.json -{ - "web": "python -m SimpleHTTPServer" -} -$ ./procmon -procfile example/procfile.json -web | Starting web with python -m SimpleHTTPServer -``` - -You can then access http://localhost:8000 to see the logs. You can -also kill the child process and see `procmon` recognizing it has -exited and exit itself. diff --git a/vendor/github.com/ionrock/procs/builder.go b/vendor/github.com/ionrock/procs/builder.go deleted file mode 100644 index 85c68391c..000000000 --- a/vendor/github.com/ionrock/procs/builder.go +++ /dev/null @@ -1,58 +0,0 @@ -package procs - -import ( - "os" - "strings" -) - -// Builder helps construct commands using templates. -type Builder struct { - Context map[string]string - Templates []string -} - -func (b *Builder) getConfig(ctx map[string]string) func(string) string { - return func(key string) string { - if v, ok := ctx[key]; ok { - return v - } - return "" - } -} - -func (b *Builder) expand(v string, ctx map[string]string) string { - return os.Expand(v, b.getConfig(ctx)) -} - -// Command returns the result of the templates as a single string. -func (b *Builder) Command() string { - parts := []string{} - for _, t := range b.Templates { - parts = append(parts, b.expand(t, b.Context)) - } - - return strings.Join(parts, " ") -} - -// CommandContext returns the result of the templates as a single -// string, but allows providing an environment context as a -// map[string]string for expansions. -func (b *Builder) CommandContext(ctx map[string]string) string { - // Build our environment context by starting with our Builder - // context and overlay the passed in context map. - env := make(map[string]string) - for k, v := range b.Context { - env[k] = b.expand(v, b.Context) - } - - for k, v := range ctx { - env[k] = b.expand(v, env) - } - - parts := []string{} - for _, t := range b.Templates { - parts = append(parts, b.expand(t, env)) - } - - return strings.Join(parts, " ") -} diff --git a/vendor/github.com/ionrock/procs/builder_test.go b/vendor/github.com/ionrock/procs/builder_test.go deleted file mode 100644 index a093eeb09..000000000 --- a/vendor/github.com/ionrock/procs/builder_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package procs_test - -import ( - "testing" - - "github.com/ionrock/procs" -) - -func TestCommandContext(t *testing.T) { - b := &procs.Builder{ - Context: map[string]string{ - "options": "-Fj -s https://example.com/chef -k knife.pem", - }, - - Templates: []string{ - "knife", - "${model} ${action}", - "${args}", - "${options}", - }, - } - - cmd := b.CommandContext(map[string]string{ - "model": "data bag", - "action": "from file", - "args": "foo data_bags/foo/bar.json", - }) - - expected := "knife data bag from file foo data_bags/foo/bar.json -Fj -s https://example.com/chef -k knife.pem" - if cmd != expected { - t.Fatalf("failed building command: %q != %q", cmd, expected) - } -} - -func TestCommand(t *testing.T) { - b := &procs.Builder{ - Context: map[string]string{ - "options": "-Fj -s https://example.com/chef -k knife.pem", - "model": "data bag", - "action": "from file", - "args": "foo data_bags/foo/bar.json", - }, - - Templates: []string{ - "knife", - "${model} ${action}", - "${args}", - "${options}", - }, - } - - cmd := b.CommandContext(map[string]string{}) - - expected := "knife data bag from file foo data_bags/foo/bar.json -Fj -s https://example.com/chef -k knife.pem" - if cmd != expected { - t.Fatalf("failed building command: %q != %q", cmd, expected) - } -} diff --git a/vendor/github.com/ionrock/procs/env.go b/vendor/github.com/ionrock/procs/env.go deleted file mode 100644 index 3cf93f76d..000000000 --- a/vendor/github.com/ionrock/procs/env.go +++ /dev/null @@ -1,48 +0,0 @@ -package procs - -import ( - "fmt" - "os" - "strings" -) - -// ParseEnv takes an environment []string and converts it to a map[string]string. -func ParseEnv(environ []string) map[string]string { - env := make(map[string]string) - for _, e := range environ { - pair := strings.SplitN(e, "=", 2) - - // There is a chance we can get an env with empty values - if len(pair) == 2 { - env[pair[0]] = pair[1] - } - } - return env -} - -// Env takes a map[string]string and converts it to a []string that -// can be used with exec.Cmd. The useEnv boolean flag will include the -// current process environment, overlaying the provided env -// map[string]string. -func Env(env map[string]string, useEnv bool) []string { - envlist := []string{} - - // update our env by loading our env and overriding any values in - // the provided env. - if useEnv { - environ := ParseEnv(os.Environ()) - for k, v := range env { - environ[k] = v - } - env = environ - } - - for key, val := range env { - if key == "" { - continue - } - envlist = append(envlist, fmt.Sprintf("%s=%s", key, val)) - } - - return envlist -} diff --git a/vendor/github.com/ionrock/procs/env_test.go b/vendor/github.com/ionrock/procs/env_test.go deleted file mode 100644 index 1b2a0498b..000000000 --- a/vendor/github.com/ionrock/procs/env_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package procs_test - -import ( - "fmt" - "os" - "os/exec" - "strings" - "testing" - - "github.com/ionrock/procs" -) - -func TestParseEnv(t *testing.T) { - env := []string{ - "FOO=bar", - "BAZ=`echo 'hello=world'`", - } - - m := procs.ParseEnv(env) - - v, ok := m["FOO"] - if !ok { - t.Errorf("error missing FOO from env: %#v", m) - } - - if v != "bar" { - t.Errorf("error FOO != bar: %s", v) - } - - v, ok = m["BAZ"] - - if !ok { - t.Errorf("error missing BAZ from env: %#v", m) - } - - expectBaz := "`echo 'hello=world'`" - if v != expectBaz { - t.Errorf("error BAZ != %s: %s", expectBaz, v) - } -} - -func TestEnvBuilder(t *testing.T) { - env := procs.Env(map[string]string{ - "FOO": "bar", - "BAZ": "hello world", - }, false) - - if len(env) != 2 { - t.Errorf("error loading env: %s", env) - } -} - -func helperEnvCommand(env map[string]string) *exec.Cmd { - cmd := exec.Command(os.Args[0], "-test.run=TestEnvBuilderOverrides") - cmd.Env = procs.Env(env, false) - return cmd -} - -func TestEnvBuilderOverrides(t *testing.T) { - if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { - return - } - for _, envvar := range procs.Env(map[string]string{"FOO": "override"}, true) { - fmt.Println(envvar) - } -} - -func TestEnvBuilderWithEnv(t *testing.T) { - cmd := helperEnvCommand(map[string]string{ - "GO_WANT_HELPER_PROCESS": "1", - "FOO": "default", - }) - out, err := cmd.Output() - if err != nil { - t.Fatalf("error running helper: %s", err) - } - - env := procs.ParseEnv(strings.Split(string(out), "\n")) - - if env["FOO"] != "override" { - t.Errorf("error overriding envvar: %s", string(out)) - } -} diff --git a/vendor/github.com/ionrock/procs/example_basic_test.go b/vendor/github.com/ionrock/procs/example_basic_test.go deleted file mode 100644 index 8d2adaf06..000000000 --- a/vendor/github.com/ionrock/procs/example_basic_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package procs_test - -import ( - "fmt" - - "github.com/ionrock/procs" -) - -func Example() { - - b := procs.Builder{ - Context: map[string]string{ - "NAME": "eric", - }, - Templates: []string{ - "echo $NAME |", - "grep $NAME", - }, - } - - cmd := b.Command() - - fmt.Println(cmd) - - p := procs.NewProcess(cmd) - - p.Run() - out, _ := p.Output() - fmt.Println(string(out)) - // Output: - // echo eric | grep eric - // eric -} diff --git a/vendor/github.com/ionrock/procs/example_predefined_cmds_test.go b/vendor/github.com/ionrock/procs/example_predefined_cmds_test.go deleted file mode 100644 index 3dfd64015..000000000 --- a/vendor/github.com/ionrock/procs/example_predefined_cmds_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package procs_test - -import ( - "fmt" - "os/exec" - - "github.com/ionrock/procs" -) - -func Example_predefinedCmds() { - p := procs.Process{ - Cmds: []*exec.Cmd{ - exec.Command("echo", "foo"), - exec.Command("grep", "foo"), - }, - } - - p.Run() - out, _ := p.Output() - fmt.Println(string(out)) - // Output: - // foo -} diff --git a/vendor/github.com/ionrock/procs/examples_test.go b/vendor/github.com/ionrock/procs/examples_test.go deleted file mode 100644 index c9b43cc1c..000000000 --- a/vendor/github.com/ionrock/procs/examples_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package procs_test - -import ( - "fmt" - - "github.com/ionrock/procs" -) - -func ExampleSplitCommand() { - parts := procs.SplitCommand("echo 'hello world'") - for i, p := range parts { - fmt.Printf("%d %s\n", i+1, p) - } - - // Output: - // 1 echo - // 2 hello world -} - -func ExampleSplitCommandEnv() { - env := map[string]string{ - "GREETING": "hello", - "NAME": "world!", - "PASSWORD": "secret", - } - - getenv := func(key string) string { - if v, ok := env[key]; ok && key != "PASSWORD" { - return v - } - return "" - } - - parts := procs.SplitCommandEnv("echo '$GREETING $NAME $PASSWORD'", getenv) - - for i, p := range parts { - fmt.Printf("%d %s\n", i+1, p) - } - - // Output: - // 1 echo - // 2 hello world! -} diff --git a/vendor/github.com/ionrock/procs/manager.go b/vendor/github.com/ionrock/procs/manager.go deleted file mode 100644 index 91169e300..000000000 --- a/vendor/github.com/ionrock/procs/manager.go +++ /dev/null @@ -1,119 +0,0 @@ -package procs - -import ( - "fmt" - "sync" -) - -// Manager manages a set of Processes. -type Manager struct { - Processes map[string]*Process - - lock sync.Mutex -} - -// NewManager creates a new *Manager. -func NewManager() *Manager { - return &Manager{ - Processes: make(map[string]*Process), - } - -} - -// StdoutHandler returns an OutHandler that will ensure the underlying -// process has an empty stdout buffer and logs to stdout a prefixed value -// of "$name | $line". -func (m *Manager) StdoutHandler(name string) OutHandler { - return func(line string) string { - fmt.Printf("%s | %s\n", name, line) - return "" - } -} - -// StderrHandler returns an OutHandler that will ensure the underlying -// process has an empty stderr buffer and logs to stdout a prefixed value -// of "$name | $line". -func (m *Manager) StderrHandler(name string) OutHandler { - return func(line string) string { - fmt.Printf("%s | %s\n", name, line) - return "" - } -} - -// Start and managed a new process using the default handlers from a -// string. -func (m *Manager) Start(name, cmd string) error { - m.lock.Lock() - defer m.lock.Unlock() - - p := NewProcess(cmd) - p.OutputHandler = m.StdoutHandler(name) - p.ErrHandler = m.StderrHandler(name) - err := p.Start() - if err != nil { - return err - } - - m.Processes[name] = p - return nil -} - -// StartProcess starts and manages a predifined process. -func (m *Manager) StartProcess(name string, p *Process) error { - m.lock.Lock() - defer m.lock.Unlock() - - err := p.Start() - if err != nil { - return err - } - - m.Processes[name] = p - return nil -} - -// Stop will try to stop a managed process. If the process does not -// exist, no error is returned. -func (m *Manager) Stop(name string) error { - p, ok := m.Processes[name] - // We don't mind stopping a process that doesn't exist. - if !ok { - return nil - } - - return p.Stop() -} - -// Remove will try to stop and remove a managed process. -func (m *Manager) Remove(name string) error { - m.lock.Lock() - defer m.lock.Unlock() - - err := m.Stop(name) - if err != nil { - return err - } - - // Note that if the stop fails we don't remove it from the map of - // processes to avoid losing the reference. - delete(m.Processes, name) - - return nil -} - -// Wait will block until all managed processes have finished. -func (m *Manager) Wait() error { - wg := &sync.WaitGroup{} - wg.Add(len(m.Processes)) - - for _, p := range m.Processes { - go func(proc *Process) { - defer wg.Done() - proc.Wait() - }(p) - } - - wg.Wait() - - return nil -} diff --git a/vendor/github.com/ionrock/procs/manager_test.go b/vendor/github.com/ionrock/procs/manager_test.go deleted file mode 100644 index 69c999407..000000000 --- a/vendor/github.com/ionrock/procs/manager_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package procs_test - -import ( - "flag" - "fmt" - "log" - "net/http" - "os" - "testing" - - "github.com/ionrock/procs" -) - -func TestManagerStartHelper(t *testing.T) { - if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { - return - } - - port := flag.String("p", "12212", "port to serve on") - directory := flag.String("d", ".", "the directory of static file to host") - flag.Parse() - - http.Handle("/", http.FileServer(http.Dir(*directory))) - - log.Printf("Serving %s on HTTP port: %s\n", *directory, *port) - log.Fatal(http.ListenAndServe(":"+*port, nil)) - os.Exit(0) -} - -func TestManagerStart(t *testing.T) { - m := procs.NewManager() - - err := m.Start("test", fmt.Sprintf("%s -test.run=TestManagerStartHelper", os.Args[0])) - if err != nil { - t.Errorf("failed to start test process: %s", err) - } - - if len(m.Processes) != 1 { - t.Error("failed to add process") - } - - err = m.Stop("test") - if err != nil { - t.Errorf("error stopping process: %s", err) - } - - err = m.Remove("test") - if err != nil { - t.Errorf("error removing process: %s", err) - } - - if len(m.Processes) != 0 { - t.Error("failed to remove processes") - } -} diff --git a/vendor/github.com/ionrock/procs/parse.go b/vendor/github.com/ionrock/procs/parse.go deleted file mode 100644 index 8753f3878..000000000 --- a/vendor/github.com/ionrock/procs/parse.go +++ /dev/null @@ -1,36 +0,0 @@ -package procs - -import ( - "log" - "os" - "strings" - - shlex "github.com/flynn/go-shlex" -) - -// SplitCommand parses a command and splits it into lexical arguments -// like a shell, returning a []string that can be used as arguments to -// exec.Command. -func SplitCommand(cmd string) []string { - return SplitCommandEnv(cmd, nil) -} - -// SplitCommandEnv parses a command and splits it into lexical -// arguments like a shell, returning a []string that can be used as -// arguments to exec.Command. It also allows providing an expansion -// function that will be used when expanding values within the parsed -// arguments. -func SplitCommandEnv(cmd string, getenv func(key string) string) []string { - parts, err := shlex.Split(strings.TrimSpace(cmd)) - if err != nil { - log.Fatal(err) - } - - if getenv != nil { - for i, p := range parts { - parts[i] = os.Expand(p, getenv) - } - } - - return parts -} diff --git a/vendor/github.com/ionrock/procs/parse_test.go b/vendor/github.com/ionrock/procs/parse_test.go deleted file mode 100644 index 1e24f6a92..000000000 --- a/vendor/github.com/ionrock/procs/parse_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package procs_test - -import ( - "testing" - - "github.com/apoydence/onpar" - . "github.com/apoydence/onpar/expect" - . "github.com/apoydence/onpar/matchers" - "github.com/ionrock/procs" -) - -func matchSplitCommand(t *testing.T, parts, expected []string) { - for i, part := range parts { - Expect(t, part).To(Equal(expected[i])) - } -} - -func TestSplitCommand(t *testing.T) { - o := onpar.New() - defer o.Run(t) - - o.Group("split with pipe", func() { - o.BeforeEach(func(t *testing.T) (*testing.T, []string, []string) { - parts := procs.SplitCommand("echo 'foo' | grep o") - expected := []string{"echo", "foo", "|", "grep", "o"} - return t, parts, expected - }) - - o.Spec("pass with a pipe", matchSplitCommand) - }) - - o.Group("replace with specific context", func() { - o.BeforeEach(func(t *testing.T) (*testing.T, []string, []string) { - parts := procs.SplitCommandEnv("echo ${FOO}", func(k string) string { - return "bar" - }) - - expected := []string{"echo", "bar"} - return t, parts, expected - }) - - o.Spec("expand values found in provided env", matchSplitCommand) - }) -} diff --git a/vendor/github.com/ionrock/procs/process.go b/vendor/github.com/ionrock/procs/process.go deleted file mode 100644 index a990e9ff2..000000000 --- a/vendor/github.com/ionrock/procs/process.go +++ /dev/null @@ -1,307 +0,0 @@ -// Procs is a library to make working with command line applications a -// little nicer. -// -// The goal is to expand on the os/exec package by providing some -// features usually accomplished in a shell, without having to resort to -// a shell. Procs also tries to make working with output simpler by -// providing a simple line handler API over working with io pipes. -// -// Finally, while the hope is that procs provides some convenience, it -// is also a goal to help make it easier to write more secure -// code. For example, avoiding a shell and the ability to manage the -// environment as a map[string]string are both measures that intend to -// make it easier to accomplish things like avoiding outputting -// secrets and opening the door for MITM attacks. With that said, it is -// always important to consider the security implications, especially -// when you are working with untrusted input or sensitive data. -package procs - -import ( - "bufio" - "bytes" - "fmt" - "io" - "log" - "os" - "os/exec" - "sync" -) - -// OutHandler defines the interface for writing output handlers for -// Process objects. -type OutHandler func(string) string - -// Process is intended to be used like exec.Cmd where possible. -type Process struct { - // CmdString takes a string and parses it into the relevant cmds - CmdString string - - // Cmds is the list of command delmited by pipes. - Cmds []*exec.Cmd - - // Env provides a map[string]string that can mutated before - // running a command. - Env map[string]string - - // Dir defines the directory the command should run in. The - // Default is the current dir. - Dir string - - // OutputHandler can be defined to perform any sort of processing - // on the output. The simple interface is to accept a string (a - // line of output) and return a string that will be included in the - // buffered output and/or output written to stdout.' - // - // For example defining the Process as: - // - // prefix := "myapp" - // p := &procs.Process{ - // OutputHandler: func(line string) string { - // return fmt.Sprintf("%s | %s", prefix, line) - // }, - // } - // - // This would prefix the stdout lines with a "myapp | ". - // - // By the default, this function is nil and will be skipped, with - // the unchanged line being added to the respective output buffer. - OutputHandler OutHandler - - // ErrHandler is a OutputHandler for stderr. - ErrHandler OutHandler - - // When no output is given, we'll buffer output in these vars. - errBuffer bytes.Buffer - outBuffer bytes.Buffer - - // When a output handler is provided, we ensure we're handling a - // single line at at time. - outputWait *sync.WaitGroup -} - -// NewProcess creates a new *Process from a command string. -// -// It is assumed that the user will mutate the resulting *Process by -// setting the necessary attributes. -func NewProcess(command string) *Process { - return &Process{CmdString: command} -} - -// internal expand method to use the proc env. -func (p *Process) expand(s string) string { - return os.Expand(s, func(key string) string { - v, _ := p.Env[key] - return v - }) -} - -// addCmd adds a new command to the list of commands, ensuring the Dir -// and Env have been added to the underlying *exec.Cmd instances. -func (p *Process) addCmd(cmdparts []string) { - var cmd *exec.Cmd - if len(cmdparts) == 1 { - cmd = exec.Command(cmdparts[0]) - } else { - cmd = exec.Command(cmdparts[0], cmdparts[1:]...) - } - - if p.Dir != "" { - cmd.Dir = p.Dir - } - - if p.Env != nil { - env := []string{} - for k, v := range p.Env { - env = append(env, fmt.Sprintf("%s=%s", k, p.expand(v))) - } - - cmd.Env = env - } - - p.Cmds = append(p.Cmds, cmd) -} - -// findCmds parses the CmdString to find the commands that should be -// run by spliting the lexically parsed command by pipes ("|"). -func (p *Process) findCmds() { - // Skip if the cmd set is already set. This allows manual creation - // of piped commands. - if len(p.Cmds) > 0 { - return - } - - if p.CmdString == "" { - return - } - - parts := SplitCommand(p.CmdString) - for i := range parts { - parts[i] = p.expand(parts[i]) - } - - cmd := []string{} - for _, part := range parts { - if part == "|" { - p.addCmd(cmd) - cmd = []string{} - } else { - cmd = append(cmd, part) - } - } - - p.addCmd(cmd) -} - -// lineReader takes will read a line in the io.Reader and write to the -// Process output buffer and use any OutputHandler that exists. -func (p *Process) lineReader(wg *sync.WaitGroup, r io.Reader, w *bytes.Buffer, handler OutHandler) { - defer wg.Done() - - reader := bufio.NewReader(r) - var buffer bytes.Buffer - - for { - buf := make([]byte, 1024) - - n, err := reader.Read(buf) - if err != nil { - return - } - - buf = buf[:n] - - for { - i := bytes.IndexByte(buf, '\n') - if i < 0 { - break - } - - buffer.Write(buf[0:i]) - outLine := buffer.String() - if handler != nil { - outLine = handler(outLine) - } - w.WriteString(outLine) - buffer.Reset() - buf = buf[i+1:] - } - buffer.Write(buf) - } -} - -// checkErr shortens the creation of the pipes by bailing out with a -// log.Fatal. -func checkErr(msg string, err error) { - if err != nil { - log.Fatalf("%s: %s", msg, err) - } -} - -func (p *Process) setupPipes() error { - last := len(p.Cmds) - 1 - - if last != 0 { - for i, cmd := range p.Cmds[:last] { - var err error - - p.Cmds[i+1].Stdin, err = cmd.StdoutPipe() - if err != nil { - fmt.Printf("error creating stdout pipe: %s\n", err) - return err - } - - cmd.Stderr = &p.errBuffer - } - } - - cmd := p.Cmds[last] - stdout, err := cmd.StdoutPipe() - if err != nil { - fmt.Printf("error creating stdout pipe: %s\n", err) - return err - } - - stderr, err := cmd.StderrPipe() - if err != nil { - fmt.Printf("error creating stderr pipe: %s\n", err) - return err - } - - p.outputWait = new(sync.WaitGroup) - p.outputWait.Add(2) - - // These close the stdout/err channels - go p.lineReader(p.outputWait, stdout, &p.outBuffer, p.OutputHandler) - go p.lineReader(p.outputWait, stderr, &p.errBuffer, p.ErrHandler) - - return nil -} - -// Run executes the cmds and returns the output as a string and any error. -func (p *Process) Run() error { - if err := p.Start(); err != nil { - return err - } - - return p.Wait() -} - -// Start will start the list of cmds. -func (p *Process) Start() error { - p.findCmds() - p.setupPipes() - - for i, cmd := range p.Cmds { - err := cmd.Start() - if err != nil { - defer func() { - for _, precmd := range p.Cmds[0:i] { - precmd.Wait() - } - }() - return err - } - } - - return nil -} - -// Wait will block, waiting for the commands to finish. -func (p *Process) Wait() error { - if p.outputWait != nil { - p.outputWait.Wait() - } - - var err error - for _, cmd := range p.Cmds { - err = cmd.Wait() - } - return err -} - -// Stop tries to stop the process. -func (p *Process) Stop() error { - for _, cmd := range p.Cmds { - // ProcessState means it is already exited. - if cmd.ProcessState != nil { - continue - } - - err := cmd.Process.Kill() - if err != nil { - return err - } - } - - return nil -} - -// Output returns the buffered output as []byte. -func (p *Process) Output() ([]byte, error) { - return p.outBuffer.Bytes(), nil -} - -// ErrOutput returns the buffered stderr as []byte -func (p *Process) ErrOutput() ([]byte, error) { - return p.errBuffer.Bytes(), nil -} diff --git a/vendor/github.com/ionrock/procs/process_test.go b/vendor/github.com/ionrock/procs/process_test.go deleted file mode 100644 index d66c5bd66..000000000 --- a/vendor/github.com/ionrock/procs/process_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package procs_test - -import ( - "bytes" - "fmt" - "os" - "os/exec" - "testing" - - "github.com/ionrock/procs" -) - -func newProcess() *procs.Process { - return &procs.Process{ - Cmds: []*exec.Cmd{ - exec.Command("echo", "foo"), - exec.Command("grep", "foo"), - }, - } -} - -func TestProcess(t *testing.T) { - p := newProcess() - - err := p.Run() - if err != nil { - t.Fatalf("error running program: %s", err) - } - - out, _ := p.Output() - if !bytes.Equal(bytes.TrimSpace(out), []byte("foo")) { - t.Errorf("wrong output: expected foo but got %s", out) - } -} - -func TestProcessWithOutput(t *testing.T) { - p := newProcess() - - p.OutputHandler = func(line string) string { - return fmt.Sprintf("x | %s", line) - } - - err := p.Run() - - if err != nil { - t.Fatalf("error running program: %s", err) - } - expected := []byte("x | foo") - out, _ := p.Output() - if !bytes.Equal(bytes.TrimSpace(out), expected) { - t.Errorf("wrong output: expected %q but got %q", expected, out) - } -} - -func TestProcessStartAndWait(t *testing.T) { - p := newProcess() - - p.Start() - p.Wait() - - out, _ := p.Output() - expected := []byte("foo") - if !bytes.Equal(bytes.TrimSpace(out), expected) { - t.Errorf("wrong output: expected %q but got %q", expected, out) - } -} - -func TestProcessStartAndWaitWithOutput(t *testing.T) { - p := newProcess() - p.OutputHandler = func(line string) string { - return fmt.Sprintf("x | %s", line) - } - - p.Start() - p.Wait() - - out, _ := p.Output() - expected := []byte("x | foo") - if !bytes.Equal(bytes.TrimSpace(out), expected) { - t.Errorf("wrong output: expected %q but got %q", expected, out) - } -} - -func TestProcessFromString(t *testing.T) { - p := procs.NewProcess("echo 'foo'") - err := p.Run() - if err != nil { - t.Fatalf("error running program: %s", err) - } - out, _ := p.Output() - if !bytes.Equal(bytes.TrimSpace(out), []byte("foo")) { - t.Errorf("wrong output: expected foo but got %s", out) - } -} - -func TestProcessFromStringWithPipe(t *testing.T) { - p := procs.NewProcess("echo 'foo' | grep foo") - err := p.Run() - if err != nil { - t.Fatalf("error running program: %s", err) - } - - out, _ := p.Output() - if !bytes.Equal(bytes.TrimSpace(out), []byte("foo")) { - t.Errorf("wrong output: expected foo but got %s", out) - } -} - -func TestStderrOutput(t *testing.T) { - if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { - return - } - - fmt.Fprintln(os.Stdout, "stdout output") - fmt.Fprintln(os.Stderr, "stderr output") - os.Exit(1) -} - -func TestProcessPipeWithFailures(t *testing.T) { - - // This will run a piped command with a failure part way - // through. We want to be sure we get output on stderr. - p := procs.NewProcess(fmt.Sprintf("echo 'foo' | %s -test.run=TestStderrOutput | grep foo", os.Args[0])) - p.Env = map[string]string{"GO_WANT_HELPER_PROCESS": "1"} - - err := p.Run() - if err == nil { - t.Fatal("expected error running program") - - } - - out, _ := p.Output() - expected := []byte("") // expecting no output b/c the grep foo won't run - if !bytes.Equal(out, expected) { - t.Errorf("wrong stdout output: expected '%s' but got '%s'", expected, out) - } - - errOut, _ := p.ErrOutput() - expected = []byte("stderr output") - if !bytes.Equal(bytes.TrimSpace(errOut), expected) { - t.Errorf("wrong stderr output: expected '%s' but got '%s'", expected, out) - } -} diff --git a/vendor/vendor.json b/vendor/vendor.json index 22d3c0c8c..9edb4937e 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -8,12 +8,6 @@ "revision": "3f9db97f856818214da2e1057f8ad84803971cff", "revisionTime": "2015-05-15T14:53:56Z" }, - { - "checksumSHA1": "AcczqNgc2/sR/+4gklvFe8zs2eE=", - "path": "github.com/ionrock/procs", - "revision": "f53ef5630f1a1fd42c4e528a04cdaa81265cc66d", - "revisionTime": "2018-01-02T00:55:58Z" - }, { "checksumSHA1": "KU+GT3javo9S9oVEJfqQUKPPUwo=", "path": "github.com/kr/pty", From 18f09a14e65f360cce691559e328a459c8865aa5 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 17:06:33 +0200 Subject: [PATCH 021/118] Removed package github.com/mgutz/str for better code coverage --- pkg/commands/exec_live_default.go | 3 +- pkg/commands/os.go | 3 +- pkg/commands/string-to-args.go | 112 ++++++ vendor/github.com/mgutz/str/LICENSE | 21 - vendor/github.com/mgutz/str/doc.go | 19 - vendor/github.com/mgutz/str/funcsAO.go | 337 ---------------- vendor/github.com/mgutz/str/funcsPZ.go | 534 ------------------------- 7 files changed, 114 insertions(+), 915 deletions(-) create mode 100644 pkg/commands/string-to-args.go delete mode 100644 vendor/github.com/mgutz/str/LICENSE delete mode 100644 vendor/github.com/mgutz/str/doc.go delete mode 100644 vendor/github.com/mgutz/str/funcsAO.go delete mode 100644 vendor/github.com/mgutz/str/funcsPZ.go diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 57898a762..6c0ac3077 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -10,7 +10,6 @@ import ( "regexp" "github.com/kr/pty" - "github.com/mgutz/str" ) // RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout @@ -19,7 +18,7 @@ import ( // NOTE: If the return data is empty it won't written anything to stdin // NOTE: You don't have to include a enter in the return data this function will do that for you func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { - splitCmd := str.ToArgv(command) + splitCmd := ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) cmd.Env = os.Environ() diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 88e4f52cd..28c01813b 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -9,7 +9,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/utils" - "github.com/mgutz/str" "github.com/sirupsen/logrus" gitconfig "github.com/tcnksm/go-gitconfig" ) @@ -50,7 +49,7 @@ func NewOSCommand(log *logrus.Entry, config config.AppConfigurer) *OSCommand { // RunCommandWithOutput wrapper around commands returning their output and error func (c *OSCommand) RunCommandWithOutput(command string) (string, error) { c.Log.WithField("command", command).Info("RunCommand") - splitCmd := str.ToArgv(command) + splitCmd := ToArgv(command) c.Log.Info(splitCmd) return sanitisedCommandOutput( c.command(splitCmd[0], splitCmd[1:]...).CombinedOutput(), diff --git a/pkg/commands/string-to-args.go b/pkg/commands/string-to-args.go new file mode 100644 index 000000000..eae90e1b5 --- /dev/null +++ b/pkg/commands/string-to-args.go @@ -0,0 +1,112 @@ +package commands + +import "runtime" + +// ToArgv converts string s into an argv for exec. +func ToArgv(s string) []string { + const ( + InArg = iota + InArgQuote + OutOfArg + ) + currentState := OutOfArg + currentQuoteChar := "\x00" // to distinguish between ' and " quotations + // this allows to use "foo'bar" + currentArg := "" + argv := []string{} + + isQuote := func(c string) bool { + return c == `"` || c == `'` + } + + isEscape := func(c string) bool { + return c == `\` + } + + isWhitespace := func(c string) bool { + return c == " " || c == "\t" + } + + L := len(s) + for i := 0; i < L; i++ { + c := s[i : i+1] + + //fmt.Printf("c %s state %v arg %s argv %v i %d\n", c, currentState, currentArg, args, i) + if isQuote(c) { + switch currentState { + case OutOfArg: + currentArg = "" + fallthrough + case InArg: + currentState = InArgQuote + currentQuoteChar = c + + case InArgQuote: + if c == currentQuoteChar { + currentState = InArg + } else { + currentArg += c + } + } + + } else if isWhitespace(c) { + switch currentState { + case InArg: + argv = append(argv, currentArg) + currentState = OutOfArg + case InArgQuote: + currentArg += c + case OutOfArg: + // nothing + } + + } else if isEscape(c) { + switch currentState { + case OutOfArg: + currentArg = "" + currentState = InArg + fallthrough + case InArg: + fallthrough + case InArgQuote: + if i == L-1 { + if runtime.GOOS == "windows" { + // just add \ to end for windows + currentArg += c + } else { + panic("Escape character at end string") + } + } else { + if runtime.GOOS == "windows" { + peek := s[i+1 : i+2] + if peek != `"` { + currentArg += c + } + } else { + i++ + c = s[i : i+1] + currentArg += c + } + } + } + } else { + switch currentState { + case InArg, InArgQuote: + currentArg += c + + case OutOfArg: + currentArg = "" + currentArg += c + currentState = InArg + } + } + } + + if currentState == InArg { + argv = append(argv, currentArg) + } else if currentState == InArgQuote { + panic("Starting quote has no ending quote.") + } + + return argv +} diff --git a/vendor/github.com/mgutz/str/LICENSE b/vendor/github.com/mgutz/str/LICENSE deleted file mode 100644 index 6045e6c75..000000000 --- a/vendor/github.com/mgutz/str/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2014 Mario L. Gutierrez - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/mgutz/str/doc.go b/vendor/github.com/mgutz/str/doc.go deleted file mode 100644 index f48742a1f..000000000 --- a/vendor/github.com/mgutz/str/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// Package str is a comprehensive set of string functions to build more -// Go awesomeness. Str complements Go's standard packages and does not duplicate -// functionality found in `strings` or `strconv`. -// -// Str is based on plain functions instead of object-based methods, -// consistent with Go standard string packages. -// -// str.Between("foo", "", "") == "foo" -// -// Str supports pipelining instead of chaining -// -// s := str.Pipe("\nabcdef\n", Clean, BetweenF("a", "f"), ChompLeftF("bc")) -// -// User-defined filters can be added to the pipeline by inserting a function -// or closure that returns a function with this signature -// -// func(string) string -// -package str diff --git a/vendor/github.com/mgutz/str/funcsAO.go b/vendor/github.com/mgutz/str/funcsAO.go deleted file mode 100644 index fd17c1c12..000000000 --- a/vendor/github.com/mgutz/str/funcsAO.go +++ /dev/null @@ -1,337 +0,0 @@ -package str - -import ( - "fmt" - "html" - //"log" - "regexp" - "strings" -) - -// Verbose flag enables console output for those functions that have -// counterparts in Go's excellent stadard packages. -var Verbose = false -var templateOpen = "{{" -var templateClose = "}}" - -var beginEndSpacesRe = regexp.MustCompile("^\\s+|\\s+$") -var camelizeRe = regexp.MustCompile(`(\-|_|\s)+(.)?`) -var camelizeRe2 = regexp.MustCompile(`(\-|_|\s)+`) -var capitalsRe = regexp.MustCompile("([A-Z])") -var dashSpaceRe = regexp.MustCompile(`[-\s]+`) -var dashesRe = regexp.MustCompile("-+") -var isAlphaNumericRe = regexp.MustCompile(`[^0-9a-z\xC0-\xFF]`) -var isAlphaRe = regexp.MustCompile(`[^a-z\xC0-\xFF]`) -var nWhitespaceRe = regexp.MustCompile(`\s+`) -var notDigitsRe = regexp.MustCompile(`[^0-9]`) -var slugifyRe = regexp.MustCompile(`[^\w\s\-]`) -var spaceUnderscoreRe = regexp.MustCompile("[_\\s]+") -var spacesRe = regexp.MustCompile("[\\s\\xA0]+") -var stripPuncRe = regexp.MustCompile(`[^\w\s]|_`) -var templateRe = regexp.MustCompile(`([\-\[\]()*\s])`) -var templateRe2 = regexp.MustCompile(`\$`) -var underscoreRe = regexp.MustCompile(`([a-z\d])([A-Z]+)`) -var whitespaceRe = regexp.MustCompile(`^[\s\xa0]*$`) - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} - -// Between extracts a string between left and right strings. -func Between(s, left, right string) string { - l := len(left) - startPos := strings.Index(s, left) - if startPos < 0 { - return "" - } - endPos := IndexOf(s, right, startPos+l) - //log.Printf("%s: left %s right %s start %d end %d", s, left, right, startPos+l, endPos) - if endPos < 0 { - return "" - } else if right == "" { - return s[endPos:] - } else { - return s[startPos+l : endPos] - } -} - -// BetweenF is the filter form for Between. -func BetweenF(left, right string) func(string) string { - return func(s string) string { - return Between(s, left, right) - } -} - -// Camelize return new string which removes any underscores or dashes and convert a string into camel casing. -func Camelize(s string) string { - return camelizeRe.ReplaceAllStringFunc(s, func(val string) string { - val = strings.ToUpper(val) - val = camelizeRe2.ReplaceAllString(val, "") - return val - }) -} - -// Capitalize uppercases the first char of s and lowercases the rest. -func Capitalize(s string) string { - return strings.ToUpper(s[0:1]) + strings.ToLower(s[1:]) -} - -// CharAt returns a string from the character at the specified position. -func CharAt(s string, index int) string { - l := len(s) - shortcut := index < 0 || index > l-1 || l == 0 - if shortcut { - return "" - } - return s[index : index+1] -} - -// CharAtF is the filter form of CharAt. -func CharAtF(index int) func(string) string { - return func(s string) string { - return CharAt(s, index) - } -} - -// ChompLeft removes prefix at the start of a string. -func ChompLeft(s, prefix string) string { - if strings.HasPrefix(s, prefix) { - return s[len(prefix):] - } - return s -} - -// ChompLeftF is the filter form of ChompLeft. -func ChompLeftF(prefix string) func(string) string { - return func(s string) string { - return ChompLeft(s, prefix) - } -} - -// ChompRight removes suffix from end of s. -func ChompRight(s, suffix string) string { - if strings.HasSuffix(s, suffix) { - return s[:len(s)-len(suffix)] - } - return s -} - -// ChompRightF is the filter form of ChompRight. -func ChompRightF(suffix string) func(string) string { - return func(s string) string { - return ChompRight(s, suffix) - } -} - -// Classify returns a camelized string with the first letter upper cased. -func Classify(s string) string { - return Camelize("-" + s) -} - -// ClassifyF is the filter form of Classify. -func ClassifyF(s string) func(string) string { - return func(s string) string { - return Classify(s) - } -} - -// Clean compresses all adjacent whitespace to a single space and trims s. -func Clean(s string) string { - s = spacesRe.ReplaceAllString(s, " ") - s = beginEndSpacesRe.ReplaceAllString(s, "") - return s -} - -// Dasherize converts a camel cased string into a string delimited by dashes. -func Dasherize(s string) string { - s = strings.TrimSpace(s) - s = spaceUnderscoreRe.ReplaceAllString(s, "-") - s = capitalsRe.ReplaceAllString(s, "-$1") - s = dashesRe.ReplaceAllString(s, "-") - s = strings.ToLower(s) - return s -} - -// EscapeHTML is alias for html.EscapeString. -func EscapeHTML(s string) string { - if Verbose { - fmt.Println("Use html.EscapeString instead of EscapeHTML") - } - return html.EscapeString(s) -} - -// DecodeHTMLEntities decodes HTML entities into their proper string representation. -// DecodeHTMLEntities is an alias for html.UnescapeString -func DecodeHTMLEntities(s string) string { - if Verbose { - fmt.Println("Use html.UnescapeString instead of DecodeHTMLEntities") - } - return html.UnescapeString(s) -} - -// EnsurePrefix ensures s starts with prefix. -func EnsurePrefix(s, prefix string) string { - if strings.HasPrefix(s, prefix) { - return s - } - return prefix + s -} - -// EnsurePrefixF is the filter form of EnsurePrefix. -func EnsurePrefixF(prefix string) func(string) string { - return func(s string) string { - return EnsurePrefix(s, prefix) - } -} - -// EnsureSuffix ensures s ends with suffix. -func EnsureSuffix(s, suffix string) string { - if strings.HasSuffix(s, suffix) { - return s - } - return s + suffix -} - -// EnsureSuffixF is the filter form of EnsureSuffix. -func EnsureSuffixF(suffix string) func(string) string { - return func(s string) string { - return EnsureSuffix(s, suffix) - } -} - -// Humanize transforms s into a human friendly form. -func Humanize(s string) string { - if s == "" { - return s - } - s = Underscore(s) - var humanizeRe = regexp.MustCompile(`_id$`) - s = humanizeRe.ReplaceAllString(s, "") - s = strings.Replace(s, "_", " ", -1) - s = strings.TrimSpace(s) - s = Capitalize(s) - return s -} - -// Iif is short for immediate if. If condition is true return truthy else falsey. -func Iif(condition bool, truthy string, falsey string) string { - if condition { - return truthy - } - return falsey -} - -// IndexOf finds the index of needle in s starting from start. -func IndexOf(s string, needle string, start int) int { - l := len(s) - if needle == "" { - if start < 0 { - return 0 - } else if start < l { - return start - } else { - return l - } - } - if start < 0 || start > l-1 { - return -1 - } - pos := strings.Index(s[start:], needle) - if pos == -1 { - return -1 - } - return start + pos -} - -// IsAlpha returns true if a string contains only letters from ASCII (a-z,A-Z). Other letters from other languages are not supported. -func IsAlpha(s string) bool { - return !isAlphaRe.MatchString(strings.ToLower(s)) -} - -// IsAlphaNumeric returns true if a string contains letters and digits. -func IsAlphaNumeric(s string) bool { - return !isAlphaNumericRe.MatchString(strings.ToLower(s)) -} - -// IsLower returns true if s comprised of all lower case characters. -func IsLower(s string) bool { - return IsAlpha(s) && s == strings.ToLower(s) -} - -// IsNumeric returns true if a string contains only digits from 0-9. Other digits not in Latin (such as Arabic) are not currently supported. -func IsNumeric(s string) bool { - return !notDigitsRe.MatchString(s) -} - -// IsUpper returns true if s contains all upper case chracters. -func IsUpper(s string) bool { - return IsAlpha(s) && s == strings.ToUpper(s) -} - -// IsEmpty returns true if the string is solely composed of whitespace. -func IsEmpty(s string) bool { - if s == "" { - return true - } - return whitespaceRe.MatchString(s) -} - -// Left returns the left substring of length n. -func Left(s string, n int) string { - if n < 0 { - return Right(s, -n) - } - return Substr(s, 0, n) -} - -// LeftF is the filter form of Left. -func LeftF(n int) func(string) string { - return func(s string) string { - return Left(s, n) - } -} - -// LeftOf returns the substring left of needle. -func LeftOf(s string, needle string) string { - return Between(s, "", needle) -} - -// Letters returns an array of runes as strings so it can be indexed into. -func Letters(s string) []string { - result := []string{} - for _, r := range s { - result = append(result, string(r)) - } - return result -} - -// Lines convert windows newlines to unix newlines then convert to an Array of lines. -func Lines(s string) []string { - s = strings.Replace(s, "\r\n", "\n", -1) - return strings.Split(s, "\n") -} - -// Map maps an array's iitem through an iterator. -func Map(arr []string, iterator func(string) string) []string { - r := []string{} - for _, item := range arr { - r = append(r, iterator(item)) - } - return r -} - -// Match returns true if patterns matches the string -func Match(s, pattern string) bool { - r := regexp.MustCompile(pattern) - return r.MatchString(s) -} diff --git a/vendor/github.com/mgutz/str/funcsPZ.go b/vendor/github.com/mgutz/str/funcsPZ.go deleted file mode 100644 index e8fe43f21..000000000 --- a/vendor/github.com/mgutz/str/funcsPZ.go +++ /dev/null @@ -1,534 +0,0 @@ -package str - -import ( - "fmt" - "html" - //"log" - "math" - "regexp" - "runtime" - "strconv" - "strings" - "unicode/utf8" -) - -// Pad pads string s on both sides with c until it has length of n. -func Pad(s, c string, n int) string { - L := len(s) - if L >= n { - return s - } - n -= L - - left := strings.Repeat(c, int(math.Ceil(float64(n)/2))) - right := strings.Repeat(c, int(math.Floor(float64(n)/2))) - return left + s + right -} - -// PadF is the filter form of Pad. -func PadF(c string, n int) func(string) string { - return func(s string) string { - return Pad(s, c, n) - } -} - -// PadLeft pads s on left side with c until it has length of n. -func PadLeft(s, c string, n int) string { - L := len(s) - if L > n { - return s - } - return strings.Repeat(c, (n-L)) + s -} - -// PadLeftF is the filter form of PadLeft. -func PadLeftF(c string, n int) func(string) string { - return func(s string) string { - return PadLeft(s, c, n) - } -} - -// PadRight pads s on right side with c until it has length of n. -func PadRight(s, c string, n int) string { - L := len(s) - if L > n { - return s - } - return s + strings.Repeat(c, n-L) -} - -// PadRightF is the filter form of Padright -func PadRightF(c string, n int) func(string) string { - return func(s string) string { - return PadRight(s, c, n) - } -} - -// Pipe pipes s through one or more string filters. -func Pipe(s string, funcs ...func(string) string) string { - for _, fn := range funcs { - s = fn(s) - } - return s -} - -// QuoteItems quotes all items in array, mostly for debugging. -func QuoteItems(arr []string) []string { - return Map(arr, func(s string) string { - return strconv.Quote(s) - }) -} - -// ReplaceF is the filter form of strings.Replace. -func ReplaceF(old, new string, n int) func(string) string { - return func(s string) string { - return strings.Replace(s, old, new, n) - } -} - -// ReplacePattern replaces string with regexp string. -// ReplacePattern returns a copy of src, replacing matches of the Regexp with the replacement string repl. Inside repl, $ signs are interpreted as in Expand, so for instance $1 represents the text of the first submatch. -func ReplacePattern(s, pattern, repl string) string { - r := regexp.MustCompile(pattern) - return r.ReplaceAllString(s, repl) -} - -// ReplacePatternF is the filter form of ReplaceRegexp. -func ReplacePatternF(pattern, repl string) func(string) string { - return func(s string) string { - return ReplacePattern(s, pattern, repl) - } -} - -// Reverse a string -func Reverse(s string) string { - cs := make([]rune, utf8.RuneCountInString(s)) - i := len(cs) - for _, c := range s { - i-- - cs[i] = c - } - return string(cs) -} - -// Right returns the right substring of length n. -func Right(s string, n int) string { - if n < 0 { - return Left(s, -n) - } - return Substr(s, len(s)-n, n) -} - -// RightF is the Filter version of Right. -func RightF(n int) func(string) string { - return func(s string) string { - return Right(s, n) - } -} - -// RightOf returns the substring to the right of prefix. -func RightOf(s string, prefix string) string { - return Between(s, prefix, "") -} - -// SetTemplateDelimiters sets the delimiters for Template function. Defaults to "{{" and "}}" -func SetTemplateDelimiters(opening, closing string) { - templateOpen = opening - templateClose = closing -} - -// Slice slices a string. If end is negative then it is the from the end -// of the string. -func Slice(s string, start, end int) string { - if end > -1 { - return s[start:end] - } - L := len(s) - if L+end > 0 { - return s[start : L-end] - } - return s[start:] -} - -// SliceF is the filter for Slice. -func SliceF(start, end int) func(string) string { - return func(s string) string { - return Slice(s, start, end) - } -} - -// SliceContains determines whether val is an element in slice. -func SliceContains(slice []string, val string) bool { - if slice == nil { - return false - } - - for _, it := range slice { - if it == val { - return true - } - } - return false -} - -// SliceIndexOf gets the indx of val in slice. Returns -1 if not found. -func SliceIndexOf(slice []string, val string) int { - if slice == nil { - return -1 - } - - for i, it := range slice { - if it == val { - return i - } - } - return -1 -} - -// Slugify converts s into a dasherized string suitable for URL segment. -func Slugify(s string) string { - sl := slugifyRe.ReplaceAllString(s, "") - sl = strings.ToLower(sl) - sl = Dasherize(sl) - return sl -} - -// StripPunctuation strips puncation from string. -func StripPunctuation(s string) string { - s = stripPuncRe.ReplaceAllString(s, "") - s = nWhitespaceRe.ReplaceAllString(s, " ") - return s -} - -// StripTags strips all of the html tags or tags specified by the parameters -func StripTags(s string, tags ...string) string { - if len(tags) == 0 { - tags = append(tags, "") - } - for _, tag := range tags { - stripTagsRe := regexp.MustCompile(`(?i)<\/?` + tag + `[^<>]*>`) - s = stripTagsRe.ReplaceAllString(s, "") - } - return s -} - -// Substr returns a substring of s starting at index of length n. -func Substr(s string, index int, n int) string { - L := len(s) - if index < 0 || index >= L || s == "" { - return "" - } - end := index + n - if end >= L { - end = L - } - if end <= index { - return "" - } - return s[index:end] -} - -// SubstrF is the filter form of Substr. -func SubstrF(index, n int) func(string) string { - return func(s string) string { - return Substr(s, index, n) - } -} - -// Template is a string template which replaces template placeholders delimited -// by "{{" and "}}" with values from map. The global delimiters may be set with -// SetTemplateDelimiters. -func Template(s string, values map[string]interface{}) string { - return TemplateWithDelimiters(s, values, templateOpen, templateClose) -} - -// TemplateDelimiters is the getter for the opening and closing delimiters for Template. -func TemplateDelimiters() (opening string, closing string) { - return templateOpen, templateClose -} - -// TemplateWithDelimiters is string template with user-defineable opening and closing delimiters. -func TemplateWithDelimiters(s string, values map[string]interface{}, opening, closing string) string { - escapeDelimiter := func(delim string) string { - result := templateRe.ReplaceAllString(delim, "\\$1") - return templateRe2.ReplaceAllString(result, "\\$") - } - - openingDelim := escapeDelimiter(opening) - closingDelim := escapeDelimiter(closing) - r := regexp.MustCompile(openingDelim + `(.+?)` + closingDelim) - matches := r.FindAllStringSubmatch(s, -1) - for _, submatches := range matches { - match := submatches[0] - key := submatches[1] - //log.Printf("match %s key %s\n", match, key) - if values[key] != nil { - v := fmt.Sprintf("%v", values[key]) - s = strings.Replace(s, match, v, -1) - } - } - - return s -} - -// ToArgv converts string s into an argv for exec. -func ToArgv(s string) []string { - const ( - InArg = iota - InArgQuote - OutOfArg - ) - currentState := OutOfArg - currentQuoteChar := "\x00" // to distinguish between ' and " quotations - // this allows to use "foo'bar" - currentArg := "" - argv := []string{} - - isQuote := func(c string) bool { - return c == `"` || c == `'` - } - - isEscape := func(c string) bool { - return c == `\` - } - - isWhitespace := func(c string) bool { - return c == " " || c == "\t" - } - - L := len(s) - for i := 0; i < L; i++ { - c := s[i : i+1] - - //fmt.Printf("c %s state %v arg %s argv %v i %d\n", c, currentState, currentArg, args, i) - if isQuote(c) { - switch currentState { - case OutOfArg: - currentArg = "" - fallthrough - case InArg: - currentState = InArgQuote - currentQuoteChar = c - - case InArgQuote: - if c == currentQuoteChar { - currentState = InArg - } else { - currentArg += c - } - } - - } else if isWhitespace(c) { - switch currentState { - case InArg: - argv = append(argv, currentArg) - currentState = OutOfArg - case InArgQuote: - currentArg += c - case OutOfArg: - // nothing - } - - } else if isEscape(c) { - switch currentState { - case OutOfArg: - currentArg = "" - currentState = InArg - fallthrough - case InArg: - fallthrough - case InArgQuote: - if i == L-1 { - if runtime.GOOS == "windows" { - // just add \ to end for windows - currentArg += c - } else { - panic("Escape character at end string") - } - } else { - if runtime.GOOS == "windows" { - peek := s[i+1 : i+2] - if peek != `"` { - currentArg += c - } - } else { - i++ - c = s[i : i+1] - currentArg += c - } - } - } - } else { - switch currentState { - case InArg, InArgQuote: - currentArg += c - - case OutOfArg: - currentArg = "" - currentArg += c - currentState = InArg - } - } - } - - if currentState == InArg { - argv = append(argv, currentArg) - } else if currentState == InArgQuote { - panic("Starting quote has no ending quote.") - } - - return argv -} - -// ToBool fuzzily converts truthy values. -func ToBool(s string) bool { - s = strings.ToLower(s) - return s == "true" || s == "yes" || s == "on" || s == "1" -} - -// ToBoolOr parses s as a bool or returns defaultValue. -func ToBoolOr(s string, defaultValue bool) bool { - b, err := strconv.ParseBool(s) - if err != nil { - return defaultValue - } - return b -} - -// ToIntOr parses s as an int or returns defaultValue. -func ToIntOr(s string, defaultValue int) int { - n, err := strconv.Atoi(s) - if err != nil { - return defaultValue - } - return n -} - -// ToFloat32Or parses as a float32 or returns defaultValue on error. -func ToFloat32Or(s string, defaultValue float32) float32 { - f, err := strconv.ParseFloat(s, 32) - if err != nil { - return defaultValue - } - return float32(f) -} - -// ToFloat64Or parses s as a float64 or returns defaultValue. -func ToFloat64Or(s string, defaultValue float64) float64 { - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return defaultValue - } - return f -} - -// ToFloatOr parses as a float64 or returns defaultValue. -var ToFloatOr = ToFloat64Or - -// TODO This is not working yet. Go's regexp package does not have some -// of the niceities in JavaScript -// -// Truncate truncates the string, accounting for word placement and chars count -// adding a morestr (defaults to ellipsis) -// func Truncate(s, morestr string, n int) string { -// L := len(s) -// if L <= n { -// return s -// } -// -// if morestr == "" { -// morestr = "..." -// } -// -// tmpl := func(c string) string { -// if strings.ToUpper(c) != strings.ToLower(c) { -// return "A" -// } -// return " " -// } -// template := s[0 : n+1] -// var truncateRe = regexp.MustCompile(`.(?=\W*\w*$)`) -// truncateRe.ReplaceAllStringFunc(template, tmpl) // 'Hello, world' -> 'HellAA AAAAA' -// var wwRe = regexp.MustCompile(`\w\w`) -// var whitespaceRe2 = regexp.MustCompile(`\s*\S+$`) -// if wwRe.MatchString(template[len(template)-2:]) { -// template = whitespaceRe2.ReplaceAllString(template, "") -// } else { -// template = strings.TrimRight(template, " \t\n") -// } -// -// if len(template+morestr) > L { -// return s -// } -// return s[0:len(template)] + morestr -// } -// -// truncate: function(length, pruneStr) { //from underscore.string, author: github.com/rwz -// var str = this.s; -// -// length = ~~length; -// pruneStr = pruneStr || '...'; -// -// if (str.length <= length) return new this.constructor(str); -// -// var tmpl = function(c){ return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; }, -// template = str.slice(0, length+1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA' -// -// if (template.slice(template.length-2).match(/\w\w/)) -// template = template.replace(/\s*\S+$/, ''); -// else -// template = new S(template.slice(0, template.length-1)).trimRight().s; -// -// return (template+pruneStr).length > str.length ? new S(str) : new S(str.slice(0, template.length)+pruneStr); -// }, - -// Underscore returns converted camel cased string into a string delimited by underscores. -func Underscore(s string) string { - if s == "" { - return "" - } - u := strings.TrimSpace(s) - - u = underscoreRe.ReplaceAllString(u, "${1}_$2") - u = dashSpaceRe.ReplaceAllString(u, "_") - u = strings.ToLower(u) - if IsUpper(s[0:1]) { - return "_" + u - } - return u -} - -// UnescapeHTML is an alias for html.UnescapeString. -func UnescapeHTML(s string) string { - if Verbose { - fmt.Println("Use html.UnescapeString instead of UnescapeHTML") - } - return html.UnescapeString(s) -} - -// WrapHTML wraps s within HTML tag having attributes attrs. Note, -// WrapHTML does not escape s value. -func WrapHTML(s string, tag string, attrs map[string]string) string { - escapeHTMLAttributeQuotes := func(v string) string { - v = strings.Replace(v, "<", "<", -1) - v = strings.Replace(v, "&", "&", -1) - v = strings.Replace(v, "\"", """, -1) - return v - } - if tag == "" { - tag = "div" - } - el := "<" + tag - for name, val := range attrs { - el += " " + name + "=\"" + escapeHTMLAttributeQuotes(val) + "\"" - } - el += ">" + s + "" - return el -} - -// WrapHTMLF is the filter form of WrapHTML. -func WrapHTMLF(tag string, attrs map[string]string) func(string) string { - return func(s string) string { - return WrapHTML(s, tag, attrs) - } -} From 372b3336629f513c6ed3f53dbc40ab99d3d83809 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 27 Oct 2018 17:08:25 +0200 Subject: [PATCH 022/118] Added copy note at top of string-to-args.go --- pkg/commands/string-to-args.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/commands/string-to-args.go b/pkg/commands/string-to-args.go index eae90e1b5..88999e9bd 100644 --- a/pkg/commands/string-to-args.go +++ b/pkg/commands/string-to-args.go @@ -1,3 +1,5 @@ +// ToArgv is copied from github.com/mgutz/str + package commands import "runtime" From 9585f494906df62f26202b5c92d5cff027a10a36 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 29 Oct 2018 08:23:56 +0100 Subject: [PATCH 023/118] Made error handling better --- pkg/commands/exec_live_default.go | 16 ++++++++++------ pkg/commands/exec_live_win.go | 4 ++-- pkg/commands/git_test.go | 13 +++++++------ pkg/commands/os.go | 10 +++++++--- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 6c0ac3077..f8755de26 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -4,10 +4,10 @@ package commands import ( "bufio" - "errors" "os" "os/exec" "regexp" + "strings" "github.com/kr/pty" ) @@ -17,7 +17,9 @@ import ( // As return of output you need to give a string that will be written to stdin // NOTE: If the return data is empty it won't written anything to stdin // NOTE: You don't have to include a enter in the return data this function will do that for you -func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { +func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { + cmdOutput := []string{} + splitCmd := ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) @@ -27,7 +29,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s tty, err := pty.Start(cmd) if err != nil { - return errors.New(err.Error()) + return errorMessage, err } defer func() { _ = tty.Close() }() @@ -40,7 +42,9 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s scanner := bufio.NewScanner(tty) scanner.Split(bufio.ScanWords) for scanner.Scan() { - toWrite := output(re.ReplaceAllString(scanner.Text(), "")) + toOutput := re.ReplaceAllString(scanner.Text(), "") + cmdOutput = append(cmdOutput, toOutput) + toWrite := output(toOutput) if len(toWrite) > 0 { _, _ = tty.Write([]byte(toWrite + "\n")) } @@ -48,8 +52,8 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s }() if err := cmd.Wait(); err != nil { - return errors.New(err.Error()) + return strings.Join(cmdOutput, " "), err } - return nil + return errorMessage, nil } diff --git a/pkg/commands/exec_live_win.go b/pkg/commands/exec_live_win.go index 343e0dd9f..835d7da68 100644 --- a/pkg/commands/exec_live_win.go +++ b/pkg/commands/exec_live_win.go @@ -4,6 +4,6 @@ package commands // RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there // TODO: Remove this hack and replace it with a propper way to run commands live on windows -func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { - return c.RunCommand(command) +func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { + return errorMessage, c.RunCommand(command) } diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index c99479a58..bef067624 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "os/exec" + "strings" "testing" "time" @@ -1008,23 +1009,23 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Error(t, err) + assert.Nil(t, err) }, }, } - for i, s := range scenarios { + for _, s := range scenarios { t.Run(s.testName, func(t *testing.T) { gitCmd := newDummyGitCommand() gitCmd.OSCommand.command = s.command err := gitCmd.Push("test", s.forcePush, func(passOrUname string) string { return "-" }) - if err.Error() == "exit status 1" && i != 2 { - s.test(nil) - } else { - s.test(err) + errMessage := err.Error() + if strings.Contains(errMessage, "error: src refspec test does not match any.") { + err = nil } + s.test(err) }) } } diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 28c01813b..dca0c8eae 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -57,7 +57,7 @@ func (c *OSCommand) RunCommandWithOutput(command string) (string, error) { } // RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper -func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) error { +func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) (errorMessage string, err error) { return RunCommandWithOutputLiveWrapper(c, command, output) } @@ -66,7 +66,7 @@ func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) // The ask argument will be "username" or "password" and expects the user's password or username back func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) error { ttyText := "" - err := c.RunCommandWithOutputLive(command, func(word string) string { + errMessage, err := c.RunCommandWithOutputLive(command, func(word string) string { ttyText = ttyText + " " + word // detect username question @@ -87,7 +87,11 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err return "" }) if err != nil { - return err + errorCode := err.Error() + if errorCode == "exit status 128" { + errMessage = errorCode + } + return errors.New(errMessage) } return nil } From 78b2bc4f600fa8f18c8d513d2a835f09a840882c Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 31 Oct 2018 16:23:58 +0100 Subject: [PATCH 024/118] Made a better way of test pushing --- pkg/commands/git_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index bef067624..dba1874c3 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1022,7 +1022,12 @@ func TestGitCommandPush(t *testing.T) { return "-" }) errMessage := err.Error() - if strings.Contains(errMessage, "error: src refspec test does not match any.") { + cutrange := 43 + if len(errMessage) < 43 { + cutrange = len(errMessage) + } + testMessage := errMessage[:cutrange] + if strings.Contains("error: src refspec test does not match any.", testMessage) { err = nil } s.test(err) From 55322890868b576f416bf9414cac2ebb46c0c6d3 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 31 Oct 2018 17:36:20 +0100 Subject: [PATCH 025/118] Fixed some tests --- pkg/commands/git_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index dba1874c3..a8b0d1ee0 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -89,7 +89,7 @@ func TestVerifyInGitRepo(t *testing.T) { }, func(err error) { assert.Error(t, err) - assert.Regexp(t, "fatal: .ot a git repository \\(or any of the parent directories\\): \\.git", err.Error()) + assert.Regexp(t, `fatal: .ot a git repository \(or any of the parent directories\s?\/?\): \.git`, err.Error()) }, }, } @@ -247,7 +247,7 @@ func TestNewGitCommand(t *testing.T) { }, func(gitCmd *GitCommand, err error) { assert.Error(t, err) - assert.Regexp(t, "fatal: .ot a git repository \\(or any of the parent directories\\): \\.git", err.Error()) + assert.Regexp(t, `fatal: .ot a git repository ((\(or any of the parent directories \): \.git)|(\(or any parent up to mount point \/\)))`, err.Error()) }, }, { From 05f0e5120a31842a166a7159feeec442efc3a56f Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 31 Oct 2018 17:55:02 +0100 Subject: [PATCH 026/118] Fixed one text --- pkg/commands/git_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index a8b0d1ee0..41bee5078 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -247,7 +247,7 @@ func TestNewGitCommand(t *testing.T) { }, func(gitCmd *GitCommand, err error) { assert.Error(t, err) - assert.Regexp(t, `fatal: .ot a git repository ((\(or any of the parent directories \): \.git)|(\(or any parent up to mount point \/\)))`, err.Error()) + assert.Regexp(t, `fatal: .ot a git repository ((\(or any of the parent directories\): \.git)|(\(or any parent up to mount point \/\)))`, err.Error()) }, }, { From 3938138ebc6787073519d357cfdbe386aae1e20f Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 31 Oct 2018 19:25:52 +0100 Subject: [PATCH 027/118] Hopefully fixed circleci --- pkg/commands/exec_live_default.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index f8755de26..69cb7dec0 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -19,6 +19,7 @@ import ( // NOTE: You don't have to include a enter in the return data this function will do that for you func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { cmdOutput := []string{} + isAlreadyClosed := false splitCmd := ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) @@ -32,7 +33,12 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s return errorMessage, err } - defer func() { _ = tty.Close() }() + defer func() { + if !isAlreadyClosed { + isAlreadyClosed = true + _ = tty.Close() + } + }() go func() { // Regex to cleanup the command output @@ -52,6 +58,10 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s }() if err := cmd.Wait(); err != nil { + if !isAlreadyClosed { + isAlreadyClosed = true + _ = tty.Close() + } return strings.Join(cmdOutput, " "), err } From 0e53a26d6fd1674f86f9ca3352544eee2f1f3326 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Thu, 1 Nov 2018 07:06:34 +0100 Subject: [PATCH 028/118] Maybe fixed the test this time --- pkg/commands/exec_live_default.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 69cb7dec0..99ad698b5 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -8,6 +8,7 @@ import ( "os/exec" "regexp" "strings" + "sync" "github.com/kr/pty" ) @@ -19,7 +20,6 @@ import ( // NOTE: You don't have to include a enter in the return data this function will do that for you func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { cmdOutput := []string{} - isAlreadyClosed := false splitCmd := ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) @@ -33,11 +33,11 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s return errorMessage, err } + var waitForBufio sync.WaitGroup + waitForBufio.Add(1) + defer func() { - if !isAlreadyClosed { - isAlreadyClosed = true - _ = tty.Close() - } + _ = tty.Close() }() go func() { @@ -55,13 +55,11 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s _, _ = tty.Write([]byte(toWrite + "\n")) } } + waitForBufio.Done() }() if err := cmd.Wait(); err != nil { - if !isAlreadyClosed { - isAlreadyClosed = true - _ = tty.Close() - } + waitForBufio.Wait() return strings.Join(cmdOutput, " "), err } From 5470bb41211f149aebc6127119585ede927f5913 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 2 Nov 2018 09:54:54 +0100 Subject: [PATCH 029/118] Added username password detect to git pull --- pkg/commands/git.go | 4 ++-- pkg/commands/os.go | 5 ++-- pkg/gui/commit_message_panel.go | 1 + pkg/gui/files_panel.go | 41 ++++++++++++++++++++++++--------- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 0b212b1ef..1b4ec57f5 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -328,8 +328,8 @@ func (c *GitCommand) Commit(message string, amend bool) (*exec.Cmd, error) { } // Pull pulls from repo -func (c *GitCommand) Pull() error { - return c.OSCommand.RunCommand("git pull --no-edit") +func (c *GitCommand) Pull(ask func(string) string) error { + return c.OSCommand.DetectUnamePass("git pull --no-edit", ask) } // Push pushes to a branch diff --git a/pkg/commands/os.go b/pkg/commands/os.go index dca0c8eae..8a36d9851 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -87,9 +87,8 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err return "" }) if err != nil { - errorCode := err.Error() - if errorCode == "exit status 128" { - errMessage = errorCode + if errorCode := err.Error(); strings.Contains("exit status 128", errorCode) { + errMessage = "exit status 128" } return errors.New(errMessage) } diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 3f2225a7b..13ff074bc 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -84,6 +84,7 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn // wait for username/passwords input waitForGroup.Wait() + _ = gui.switchFocus(g, pushPassUnameView, currentView) return unamePassMessage } diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 5c957d217..cdd619425 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -341,31 +341,50 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error { } func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { - gui.createMessagePanel(g, v, "", gui.Tr.SLocalize("PullWait")) + if err := gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("PullWait")); err != nil { + return err + } go func() { - if err := gui.GitCommand.Pull(); err != nil { - gui.createErrorPanel(g, err.Error()) + unamePassOpend := false + err := gui.GitCommand.Pull(func(passOrUname string) string { + unamePassOpend = true + return gui.waitForPassUname(g, v, passOrUname) + }) + if unamePassOpend { + _, _ = g.SetViewOnBottom("pushPassUname") + _ = g.DeleteView("pushPassUname") + } + if err != nil { + errMessage := err.Error() + if errMessage == "exit status 128" { + errMessage = gui.Tr.SLocalize("PassUnameWrong") + } + gui.createErrorPanel(gui.g, errMessage) } else { - gui.closeConfirmationPrompt(g) - gui.refreshCommits(g) - gui.refreshStatus(g) + _ = gui.closeConfirmationPrompt(gui.g) + _ = gui.refreshCommits(gui.g) + _ = gui.refreshStatus(gui.g) } gui.refreshFiles(g) }() return nil } -func (gui *Gui) pushWithForceFlag(g *gocui.Gui, currentView *gocui.View, force bool) error { - if err := gui.createMessagePanel(gui.g, currentView, "", gui.Tr.SLocalize("PushWait")); err != nil { +func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error { + if err := gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("PushWait")); err != nil { return err } go func() { + unamePassOpend := false branchName := gui.State.Branches[0].Name err := gui.GitCommand.Push(branchName, force, func(passOrUname string) string { - return gui.waitForPassUname(g, currentView, passOrUname) + unamePassOpend = true + return gui.waitForPassUname(g, v, passOrUname) }) - _, _ = g.SetViewOnBottom("pushPassUname") - _ = g.DeleteView("pushPassUname") + if unamePassOpend { + _, _ = g.SetViewOnBottom("pushPassUname") + _ = g.DeleteView("pushPassUname") + } if err != nil { errMessage := err.Error() if errMessage == "exit status 128" { From cb9ad5bc730f3f4ffe5f1d6c1aeff74e354c4962 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 2 Nov 2018 10:06:10 +0100 Subject: [PATCH 030/118] Fixed golangcibot surgestion --- pkg/gui/files_panel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index cdd619425..757271754 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -359,7 +359,7 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { if errMessage == "exit status 128" { errMessage = gui.Tr.SLocalize("PassUnameWrong") } - gui.createErrorPanel(gui.g, errMessage) + _ = gui.createErrorPanel(gui.g, errMessage) } else { _ = gui.closeConfirmationPrompt(gui.g) _ = gui.refreshCommits(gui.g) From af54d7f01580c11a180d6467c74cd24d4a8edbb9 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 2 Nov 2018 15:07:10 +0100 Subject: [PATCH 031/118] Fixed view not defined error with git push and pull --- pkg/gui/commit_message_panel.go | 1 - pkg/gui/files_panel.go | 40 +++++++++++++++++---------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 13ff074bc..3f2225a7b 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -84,7 +84,6 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn // wait for username/passwords input waitForGroup.Wait() - _ = gui.switchFocus(g, pushPassUnameView, currentView) return unamePassMessage } diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 757271754..64efd5b9c 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -350,20 +350,21 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { unamePassOpend = true return gui.waitForPassUname(g, v, passOrUname) }) - if unamePassOpend { - _, _ = g.SetViewOnBottom("pushPassUname") - _ = g.DeleteView("pushPassUname") - } if err != nil { errMessage := err.Error() if errMessage == "exit status 128" { errMessage = gui.Tr.SLocalize("PassUnameWrong") } - _ = gui.createErrorPanel(gui.g, errMessage) - } else { - _ = gui.closeConfirmationPrompt(gui.g) - _ = gui.refreshCommits(gui.g) - _ = gui.refreshStatus(gui.g) + _ = gui.createErrorPanel(g, errMessage) + } + if unamePassOpend { + _, _ = g.SetViewOnBottom("pushPassUname") + _ = g.DeleteView("pushPassUname") + } + if err == nil { + _ = gui.closeConfirmationPrompt(g) + _ = gui.refreshCommits(g) + _ = gui.refreshStatus(g) } gui.refreshFiles(g) }() @@ -371,7 +372,7 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error { - if err := gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("PushWait")); err != nil { + if err := gui.createMessagePanel(g, v, "", gui.Tr.SLocalize("PushWait")); err != nil { return err } go func() { @@ -381,20 +382,21 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error unamePassOpend = true return gui.waitForPassUname(g, v, passOrUname) }) - if unamePassOpend { - _, _ = g.SetViewOnBottom("pushPassUname") - _ = g.DeleteView("pushPassUname") - } if err != nil { errMessage := err.Error() if errMessage == "exit status 128" { errMessage = gui.Tr.SLocalize("PassUnameWrong") } - _ = gui.createErrorPanel(gui.g, errMessage) - } else { - _ = gui.closeConfirmationPrompt(gui.g) - _ = gui.refreshCommits(gui.g) - _ = gui.refreshStatus(gui.g) + _ = gui.createErrorPanel(g, errMessage) + } + if unamePassOpend { + _, _ = g.SetViewOnBottom("pushPassUname") + _ = g.DeleteView("pushPassUname") + } + if err == nil { + _ = gui.closeConfirmationPrompt(g) + _ = gui.refreshCommits(g) + _ = gui.refreshStatus(g) } }() return nil From 8469239d84bc71bececc5a9027d3a3e0fe683fd6 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 3 Nov 2018 09:12:45 +0100 Subject: [PATCH 032/118] Fixed test --- pkg/commands/exec_live_default.go | 17 ++++++++++++----- pkg/commands/git_test.go | 16 +++------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 99ad698b5..88bbf2eb4 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -20,6 +20,7 @@ import ( // NOTE: You don't have to include a enter in the return data this function will do that for you func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { cmdOutput := []string{} + canAsk := true splitCmd := ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) @@ -48,17 +49,23 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s scanner := bufio.NewScanner(tty) scanner.Split(bufio.ScanWords) for scanner.Scan() { - toOutput := re.ReplaceAllString(scanner.Text(), "") - cmdOutput = append(cmdOutput, toOutput) - toWrite := output(toOutput) - if len(toWrite) > 0 { - _, _ = tty.Write([]byte(toWrite + "\n")) + // canAsk prefrents calls to output when the program is already closed + if canAsk { + toOutput := re.ReplaceAllString(scanner.Text(), "") + cmdOutput = append(cmdOutput, toOutput) + toWrite := output(toOutput) + if len(toWrite) > 0 { + _, _ = tty.Write([]byte(toWrite + "\n")) + } } } waitForBufio.Done() }() if err := cmd.Wait(); err != nil { + canAsk = false + + // waitForBufio.Wait() return strings.Join(cmdOutput, " "), err } diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 41bee5078..7f111997c 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -5,7 +5,6 @@ import ( "io/ioutil" "os" "os/exec" - "strings" "testing" "time" @@ -983,7 +982,7 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Nil(t, err) + assert.Equal(t, "exit status 128", err.Error()) }, }, { @@ -996,7 +995,7 @@ func TestGitCommandPush(t *testing.T) { }, true, func(err error) { - assert.Nil(t, err) + assert.Equal(t, "exit status 128", err.Error()) }, }, { @@ -1009,7 +1008,7 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Nil(t, err) + assert.Equal(t, "exit status 128", err.Error()) }, }, } @@ -1021,15 +1020,6 @@ func TestGitCommandPush(t *testing.T) { err := gitCmd.Push("test", s.forcePush, func(passOrUname string) string { return "-" }) - errMessage := err.Error() - cutrange := 43 - if len(errMessage) < 43 { - cutrange = len(errMessage) - } - testMessage := errMessage[:cutrange] - if strings.Contains("error: src refspec test does not match any.", testMessage) { - err = nil - } s.test(err) }) } From cf1e9f79b1d1f7faad5c2ccff4536936b10f3e19 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 3 Nov 2018 09:36:38 +0100 Subject: [PATCH 033/118] hopefully fixed the test now --- pkg/commands/exec_live_default.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 88bbf2eb4..27363c3b9 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -20,7 +20,6 @@ import ( // NOTE: You don't have to include a enter in the return data this function will do that for you func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { cmdOutput := []string{} - canAsk := true splitCmd := ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) @@ -34,6 +33,19 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s return errorMessage, err } + var canAskLock sync.Mutex + canAskValue := true + canAsk := func() bool { + canAskLock.Lock() + defer canAskLock.Unlock() + return canAskValue + } + stopCanAsk := func() { + canAskLock.Lock() + defer canAskLock.Unlock() + canAskValue = false + } + var waitForBufio sync.WaitGroup waitForBufio.Add(1) @@ -50,7 +62,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s scanner.Split(bufio.ScanWords) for scanner.Scan() { // canAsk prefrents calls to output when the program is already closed - if canAsk { + if canAsk() { toOutput := re.ReplaceAllString(scanner.Text(), "") cmdOutput = append(cmdOutput, toOutput) toWrite := output(toOutput) @@ -63,9 +75,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s }() if err := cmd.Wait(); err != nil { - canAsk = false - - // + stopCanAsk() waitForBufio.Wait() return strings.Join(cmdOutput, " "), err } From ad77ac639eab9799cccdacbf02cd40a2c54a20a1 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Tue, 6 Nov 2018 20:24:10 +0100 Subject: [PATCH 034/118] Working new lines in live pty output --- pkg/commands/exec_live_default.go | 52 +++++++++++++++++++++++++++++-- pkg/commands/os.go | 5 +-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 27363c3b9..e717d972d 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -9,6 +9,7 @@ import ( "regexp" "strings" "sync" + "unicode/utf8" "github.com/kr/pty" ) @@ -33,6 +34,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s return errorMessage, err } + // canAsk makes sure there are no data races in go var canAskLock sync.Mutex canAskValue := true canAsk := func() bool { @@ -59,7 +61,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s re := regexp.MustCompile(`(^\s*)|(\s*$)`) scanner := bufio.NewScanner(tty) - scanner.Split(bufio.ScanWords) + scanner.Split(scanWordsWithNewLines) for scanner.Scan() { // canAsk prefrents calls to output when the program is already closed if canAsk() { @@ -74,7 +76,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s waitForBufio.Done() }() - if err := cmd.Wait(); err != nil { + if err = cmd.Wait(); err != nil { stopCanAsk() waitForBufio.Wait() return strings.Join(cmdOutput, " "), err @@ -82,3 +84,49 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s return errorMessage, nil } + +// scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines +func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) { + start := 0 + for width := 0; start < len(data); start += width { + var r rune + r, width = utf8.DecodeRune(data[start:]) + if !isSpace(r) { + break + } + } + for width, i := 0, start; i < len(data); i += width { + var r rune + r, width = utf8.DecodeRune(data[i:]) + if isSpace(r) { + return i + width, data[start:i], nil + } + } + if atEOF && len(data) > start { + return len(data), data[start:], nil + } + return start, nil, nil +} + +// isSpace is also copied form bufio.ScanWords and has been modiefied to also captures new lines +func isSpace(r rune) bool { + if r <= '\u00FF' { + // Obvious ASCII ones: \t through \r plus space. Plus two Latin-1 oddballs. + switch r { + case ' ', '\t', '\v', '\f': + return true + case '\u0085', '\u00A0': + return true + } + return false + } + // High-valued ones. + if '\u2000' <= r && r <= '\u200a' { + return true + } + switch r { + case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000': + return true + } + return false +} diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 8a36d9851..ab545a00b 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -87,8 +87,9 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err return "" }) if err != nil { - if errorCode := err.Error(); strings.Contains("exit status 128", errorCode) { - errMessage = "exit status 128" + if strings.Contains("exit status 128", err.Error()) { + // errMessage = "exit status 128" + errMessage = errMessage } return errors.New(errMessage) } From 7c4d360645a91199efbe5f805fe078638b500af9 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Tue, 6 Nov 2018 20:25:11 +0100 Subject: [PATCH 035/118] Better error for code 128 --- pkg/commands/os.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index ab545a00b..05b11a62e 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -88,8 +88,7 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err }) if err != nil { if strings.Contains("exit status 128", err.Error()) { - // errMessage = "exit status 128" - errMessage = errMessage + errMessage = "exit status 128" } return errors.New(errMessage) } From 9c8b2412920400ed7cb24efc3d2006ce6407d5f8 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Tue, 6 Nov 2018 20:37:59 +0100 Subject: [PATCH 036/118] Removed some useless comments --- pkg/commands/exec_live_default.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index e717d972d..88a80addb 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -86,6 +86,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s } // scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines +// For specific comments about this function take a look at: bufio.ScanWords func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) { start := 0 for width := 0; start < len(data); start += width { @@ -108,10 +109,10 @@ func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, return start, nil, nil } -// isSpace is also copied form bufio.ScanWords and has been modiefied to also captures new lines +// isSpace is also copied from the bufio package and has been modified to also captures new lines +// For specific comments about this function take a look at: bufio.isSpace func isSpace(r rune) bool { if r <= '\u00FF' { - // Obvious ASCII ones: \t through \r plus space. Plus two Latin-1 oddballs. switch r { case ' ', '\t', '\v', '\f': return true @@ -120,7 +121,6 @@ func isSpace(r rune) bool { } return false } - // High-valued ones. if '\u2000' <= r && r <= '\u200a' { return true } From 1fd8cadd9efdc3e1a624f0d918042d69eef3853f Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 08:14:35 +0100 Subject: [PATCH 037/118] Replaced regex with trim --- pkg/commands/exec_live_default.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 88a80addb..0297cdcd1 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -6,7 +6,6 @@ import ( "bufio" "os" "os/exec" - "regexp" "strings" "sync" "unicode/utf8" @@ -56,16 +55,12 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s }() go func() { - // Regex to cleanup the command output - // sometimes the output words include unneeded spaces at eatch end of the string - re := regexp.MustCompile(`(^\s*)|(\s*$)`) - scanner := bufio.NewScanner(tty) scanner.Split(scanWordsWithNewLines) for scanner.Scan() { // canAsk prefrents calls to output when the program is already closed if canAsk() { - toOutput := re.ReplaceAllString(scanner.Text(), "") + toOutput := strings.Trim(scanner.Text(), " ") cmdOutput = append(cmdOutput, toOutput) toWrite := output(toOutput) if len(toWrite) > 0 { From 5ae0e75e5e112a9dd9338d44699cbeaaa13a4370 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 08:39:09 +0100 Subject: [PATCH 038/118] Switched to channels instaid of a mutex --- pkg/commands/exec_live_default.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 0297cdcd1..9e39585bc 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -33,19 +33,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s return errorMessage, err } - // canAsk makes sure there are no data races in go - var canAskLock sync.Mutex - canAskValue := true - canAsk := func() bool { - canAskLock.Lock() - defer canAskLock.Unlock() - return canAskValue - } - stopCanAsk := func() { - canAskLock.Lock() - defer canAskLock.Unlock() - canAskValue = false - } + stopAsking := make(chan struct{}) var waitForBufio sync.WaitGroup waitForBufio.Add(1) @@ -57,9 +45,12 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s go func() { scanner := bufio.NewScanner(tty) scanner.Split(scanWordsWithNewLines) + loop: for scanner.Scan() { - // canAsk prefrents calls to output when the program is already closed - if canAsk() { + select { + case <-stopAsking: + break loop + default: toOutput := strings.Trim(scanner.Text(), " ") cmdOutput = append(cmdOutput, toOutput) toWrite := output(toOutput) @@ -72,7 +63,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s }() if err = cmd.Wait(); err != nil { - stopCanAsk() + stopAsking <- struct{}{} waitForBufio.Wait() return strings.Join(cmdOutput, " "), err } From 18bcc0df4d28f84086e9f5ecb1771e6afc582fc9 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 08:43:02 +0100 Subject: [PATCH 039/118] Fixed no error text on windows when executing a command live on windows it detects errors but did just show a empty string --- pkg/commands/exec_live_win.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/commands/exec_live_win.go b/pkg/commands/exec_live_win.go index 835d7da68..d97274279 100644 --- a/pkg/commands/exec_live_win.go +++ b/pkg/commands/exec_live_win.go @@ -5,5 +5,6 @@ package commands // RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there // TODO: Remove this hack and replace it with a propper way to run commands live on windows func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { - return errorMessage, c.RunCommand(command) + cmdOputput := c.RunCommand(command) + return cmdOputput.Error(), cmdOputput } From 500267417b9779822e908ba7997f9adb9c881cac Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 08:46:42 +0100 Subject: [PATCH 040/118] Removed some duplicated code --- pkg/commands/os.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 05b11a62e..65e25271b 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -69,21 +69,20 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err errMessage, err := c.RunCommandWithOutputLive(command, func(word string) string { ttyText = ttyText + " " + word - // detect username question - detectUname, _ := regexp.MatchString(`Username\s*for\s*'.+':`, ttyText) - if detectUname { - // reset the text and return the user's username - ttyText = "" - return ask("username") + // prompt and patterns to check if the user needs to input a username / password + prompts := map[string]string{ + "password": `Password\s*for\s*'.+':`, + "username": `Username\s*for\s*'.+':`, } - // detect password question - detectPass, _ := regexp.MatchString(`Password\s*for\s*'.+':`, ttyText) - if detectPass { - // reset the text and return the user's username - ttyText = "" - return ask("password") + for prompt, pattern := range prompts { + match, _ := regexp.MatchString(pattern, ttyText) + if match { + ttyText = "" + return ask(prompt) + } } + return "" }) if err != nil { From 834e42897d5c2190a7f108d9be5c910138c1199e Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 08:57:02 +0100 Subject: [PATCH 041/118] Switched back to github.com/mgutz/str instaid of a copy of ToArgv --- pkg/commands/exec_live_default.go | 3 +- pkg/commands/os.go | 3 +- pkg/commands/string-to-args.go | 114 ---- vendor/github.com/mgutz/str/CREDITS | 5 + vendor/github.com/mgutz/str/LICENSE | 21 + vendor/github.com/mgutz/str/README.md | 649 ++++++++++++++++++++++ vendor/github.com/mgutz/str/VERSION | 1 + vendor/github.com/mgutz/str/doc.go | 19 + vendor/github.com/mgutz/str/funcsAO.go | 337 ++++++++++++ vendor/github.com/mgutz/str/funcsPZ.go | 534 ++++++++++++++++++ vendor/github.com/mgutz/str/str_test.go | 696 ++++++++++++++++++++++++ vendor/vendor.json | 6 + 12 files changed, 2272 insertions(+), 116 deletions(-) delete mode 100644 pkg/commands/string-to-args.go create mode 100644 vendor/github.com/mgutz/str/CREDITS create mode 100644 vendor/github.com/mgutz/str/LICENSE create mode 100644 vendor/github.com/mgutz/str/README.md create mode 100644 vendor/github.com/mgutz/str/VERSION create mode 100644 vendor/github.com/mgutz/str/doc.go create mode 100644 vendor/github.com/mgutz/str/funcsAO.go create mode 100644 vendor/github.com/mgutz/str/funcsPZ.go create mode 100644 vendor/github.com/mgutz/str/str_test.go diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 9e39585bc..685974a1b 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -11,6 +11,7 @@ import ( "unicode/utf8" "github.com/kr/pty" + "github.com/mgutz/str" ) // RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout @@ -21,7 +22,7 @@ import ( func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { cmdOutput := []string{} - splitCmd := ToArgv(command) + splitCmd := str.ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) cmd.Env = os.Environ() diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 65e25271b..7f2ddde25 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -9,6 +9,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/mgutz/str" "github.com/sirupsen/logrus" gitconfig "github.com/tcnksm/go-gitconfig" ) @@ -49,7 +50,7 @@ func NewOSCommand(log *logrus.Entry, config config.AppConfigurer) *OSCommand { // RunCommandWithOutput wrapper around commands returning their output and error func (c *OSCommand) RunCommandWithOutput(command string) (string, error) { c.Log.WithField("command", command).Info("RunCommand") - splitCmd := ToArgv(command) + splitCmd := str.ToArgv(command) c.Log.Info(splitCmd) return sanitisedCommandOutput( c.command(splitCmd[0], splitCmd[1:]...).CombinedOutput(), diff --git a/pkg/commands/string-to-args.go b/pkg/commands/string-to-args.go deleted file mode 100644 index 88999e9bd..000000000 --- a/pkg/commands/string-to-args.go +++ /dev/null @@ -1,114 +0,0 @@ -// ToArgv is copied from github.com/mgutz/str - -package commands - -import "runtime" - -// ToArgv converts string s into an argv for exec. -func ToArgv(s string) []string { - const ( - InArg = iota - InArgQuote - OutOfArg - ) - currentState := OutOfArg - currentQuoteChar := "\x00" // to distinguish between ' and " quotations - // this allows to use "foo'bar" - currentArg := "" - argv := []string{} - - isQuote := func(c string) bool { - return c == `"` || c == `'` - } - - isEscape := func(c string) bool { - return c == `\` - } - - isWhitespace := func(c string) bool { - return c == " " || c == "\t" - } - - L := len(s) - for i := 0; i < L; i++ { - c := s[i : i+1] - - //fmt.Printf("c %s state %v arg %s argv %v i %d\n", c, currentState, currentArg, args, i) - if isQuote(c) { - switch currentState { - case OutOfArg: - currentArg = "" - fallthrough - case InArg: - currentState = InArgQuote - currentQuoteChar = c - - case InArgQuote: - if c == currentQuoteChar { - currentState = InArg - } else { - currentArg += c - } - } - - } else if isWhitespace(c) { - switch currentState { - case InArg: - argv = append(argv, currentArg) - currentState = OutOfArg - case InArgQuote: - currentArg += c - case OutOfArg: - // nothing - } - - } else if isEscape(c) { - switch currentState { - case OutOfArg: - currentArg = "" - currentState = InArg - fallthrough - case InArg: - fallthrough - case InArgQuote: - if i == L-1 { - if runtime.GOOS == "windows" { - // just add \ to end for windows - currentArg += c - } else { - panic("Escape character at end string") - } - } else { - if runtime.GOOS == "windows" { - peek := s[i+1 : i+2] - if peek != `"` { - currentArg += c - } - } else { - i++ - c = s[i : i+1] - currentArg += c - } - } - } - } else { - switch currentState { - case InArg, InArgQuote: - currentArg += c - - case OutOfArg: - currentArg = "" - currentArg += c - currentState = InArg - } - } - } - - if currentState == InArg { - argv = append(argv, currentArg) - } else if currentState == InArgQuote { - panic("Starting quote has no ending quote.") - } - - return argv -} diff --git a/vendor/github.com/mgutz/str/CREDITS b/vendor/github.com/mgutz/str/CREDITS new file mode 100644 index 000000000..ddb244c30 --- /dev/null +++ b/vendor/github.com/mgutz/str/CREDITS @@ -0,0 +1,5 @@ +* [string.js](http://stringjs.com) - I contributed several + functions to this project. + +* [bbgen.net](http://bbgen.net/blog/2011/06/string-to-argc-argv/) + diff --git a/vendor/github.com/mgutz/str/LICENSE b/vendor/github.com/mgutz/str/LICENSE new file mode 100644 index 000000000..6045e6c75 --- /dev/null +++ b/vendor/github.com/mgutz/str/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2014 Mario L. Gutierrez + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mgutz/str/README.md b/vendor/github.com/mgutz/str/README.md new file mode 100644 index 000000000..bce814bc2 --- /dev/null +++ b/vendor/github.com/mgutz/str/README.md @@ -0,0 +1,649 @@ +# str + + import "github.com/mgutz/str" + +Package str is a comprehensive set of string functions to build more Go +awesomeness. Str complements Go's standard packages and does not duplicate +functionality found in `strings` or `strconv`. + +Str is based on plain functions instead of object-based methods, consistent with +Go standard string packages. + + str.Between("foo", "", "") == "foo" + +Str supports pipelining instead of chaining + + s := str.Pipe("\nabcdef\n", Clean, BetweenF("a", "f"), ChompLeftF("bc")) + +User-defined filters can be added to the pipeline by inserting a function or +closure that returns a function with this signature + + func(string) string + +### Index + +* [Variables](#variables) +* [func Between](#func +[godoc](https://godoc.org/github.com/mgutz/str) +between) +* [func BetweenF](#func--betweenf) +* [func Camelize](#func--camelize) +* [func Capitalize](#func--capitalize) +* [func CharAt](#func--charat) +* [func CharAtF](#func--charatf) +* [func ChompLeft](#func--chompleft) +* [func ChompLeftF](#func--chompleftf) +* [func ChompRight](#func--chompright) +* [func ChompRightF](#func--chomprightf) +* [func Classify](#func--classify) +* [func ClassifyF](#func--classifyf) +* [func Clean](#func--clean) +* [func Dasherize](#func--dasherize) +* [func DecodeHTMLEntities](#func--decodehtmlentities) +* [func EnsurePrefix](#func--ensureprefix) +* [func EnsurePrefixF](#func--ensureprefixf) +* [func EnsureSuffix](#func--ensuresuffix) +* [func EnsureSuffixF](#func--ensuresuffixf) +* [func EscapeHTML](#func--escapehtml) +* [func Humanize](#func--humanize) +* [func Iif](#func--iif) +* [func IndexOf](#func--indexof) +* [func IsAlpha](#func--isalpha) +* [func IsAlphaNumeric](#func--isalphanumeric) +* [func IsEmpty](#func--isempty) +* [func IsLower](#func--islower) +* [func IsNumeric](#func--isnumeric) +* [func IsUpper](#func--isupper) +* [func Left](#func--left) +* [func LeftF](#func--leftf) +* [func LeftOf](#func--leftof) +* [func Letters](#func--letters) +* [func Lines](#func--lines) +* [func Map](#func--map) +* [func Match](#func--match) +* [func Pad](#func--pad) +* [func PadF](#func--padf) +* [func PadLeft](#func--padleft) +* [func PadLeftF](#func--padleftf) +* [func PadRight](#func--padright) +* [func PadRightF](#func--padrightf) +* [func Pipe](#func--pipe) +* [func QuoteItems](#func--quoteitems) +* [func ReplaceF](#func--replacef) +* [func ReplacePattern](#func--replacepattern) +* [func ReplacePatternF](#func--replacepatternf) +* [func Reverse](#func--reverse) +* [func Right](#func--right) +* [func RightF](#func--rightf) +* [func RightOf](#func--rightof) +* [func SetTemplateDelimiters](#func--settemplatedelimiters) +* [func Slice](#func--slice) +* [func SliceContains](#func--slicecontains) +* [func SliceF](#func--slicef) +* [func SliceIndexOf](#func--sliceindexof) +* [func Slugify](#func--slugify) +* [func StripPunctuation](#func--strippunctuation) +* [func StripTags](#func--striptags) +* [func Substr](#func--substr) +* [func SubstrF](#func--substrf) +* [func Template](#func--template) +* [func TemplateDelimiters](#func--templatedelimiters) +* [func TemplateWithDelimiters](#func--templatewithdelimiters) +* [func ToArgv](#func--toargv) +* [func ToBool](#func--tobool) +* [func ToBoolOr](#func--toboolor) +* [func ToFloat32Or](#func--tofloat32or) +* [func ToFloat64Or](#func--tofloat64or) +* [func ToIntOr](#func--tointor) +* [func Underscore](#func--underscore) +* [func UnescapeHTML](#func--unescapehtml) +* [func WrapHTML](#func--wraphtml) +* [func WrapHTMLF](#func--wraphtmlf) + + +#### Variables + +```go +var ToFloatOr = ToFloat64Or +``` +ToFloatOr parses as a float64 or returns defaultValue. + +```go +var Verbose = false +``` +Verbose flag enables console output for those functions that have counterparts +in Go's excellent stadard packages. + +#### func [Between](#between) + +```go +func Between(s, left, right string) string +``` +Between extracts a string between left and right strings. + +#### func [BetweenF](#betweenf) + +```go +func BetweenF(left, right string) func(string) string +``` +BetweenF is the filter form for Between. + +#### func [Camelize](#camelize) + +```go +func Camelize(s string) string +``` +Camelize return new string which removes any underscores or dashes and convert a +string into camel casing. + +#### func [Capitalize](#capitalize) + +```go +func Capitalize(s string) string +``` +Capitalize uppercases the first char of s and lowercases the rest. + +#### func [CharAt](#charat) + +```go +func CharAt(s string, index int) string +``` +CharAt returns a string from the character at the specified position. + +#### func [CharAtF](#charatf) + +```go +func CharAtF(index int) func(string) string +``` +CharAtF is the filter form of CharAt. + +#### func [ChompLeft](#chompleft) + +```go +func ChompLeft(s, prefix string) string +``` +ChompLeft removes prefix at the start of a string. + +#### func [ChompLeftF](#chompleftf) + +```go +func ChompLeftF(prefix string) func(string) string +``` +ChompLeftF is the filter form of ChompLeft. + +#### func [ChompRight](#chompright) + +```go +func ChompRight(s, suffix string) string +``` +ChompRight removes suffix from end of s. + +#### func [ChompRightF](#chomprightf) + +```go +func ChompRightF(suffix string) func(string) string +``` +ChompRightF is the filter form of ChompRight. + +#### func [Classify](#classify) + +```go +func Classify(s string) string +``` +Classify returns a camelized string with the first letter upper cased. + +#### func [ClassifyF](#classifyf) + +```go +func ClassifyF(s string) func(string) string +``` +ClassifyF is the filter form of Classify. + +#### func [Clean](#clean) + +```go +func Clean(s string) string +``` +Clean compresses all adjacent whitespace to a single space and trims s. + +#### func [Dasherize](#dasherize) + +```go +func Dasherize(s string) string +``` +Dasherize converts a camel cased string into a string delimited by dashes. + +#### func [DecodeHTMLEntities](#decodehtmlentities) + +```go +func DecodeHTMLEntities(s string) string +``` +DecodeHTMLEntities decodes HTML entities into their proper string +representation. DecodeHTMLEntities is an alias for html.UnescapeString + +#### func [EnsurePrefix](#ensureprefix) + +```go +func EnsurePrefix(s, prefix string) string +``` +EnsurePrefix ensures s starts with prefix. + +#### func [EnsurePrefixF](#ensureprefixf) + +```go +func EnsurePrefixF(prefix string) func(string) string +``` +EnsurePrefixF is the filter form of EnsurePrefix. + +#### func [EnsureSuffix](#ensuresuffix) + +```go +func EnsureSuffix(s, suffix string) string +``` +EnsureSuffix ensures s ends with suffix. + +#### func [EnsureSuffixF](#ensuresuffixf) + +```go +func EnsureSuffixF(suffix string) func(string) string +``` +EnsureSuffixF is the filter form of EnsureSuffix. + +#### func [EscapeHTML](#escapehtml) + +```go +func EscapeHTML(s string) string +``` +EscapeHTML is alias for html.EscapeString. + +#### func [Humanize](#humanize) + +```go +func Humanize(s string) string +``` +Humanize transforms s into a human friendly form. + +#### func [Iif](#iif) + +```go +func Iif(condition bool, truthy string, falsey string) string +``` +Iif is short for immediate if. If condition is true return truthy else falsey. + +#### func [IndexOf](#indexof) + +```go +func IndexOf(s string, needle string, start int) int +``` +IndexOf finds the index of needle in s starting from start. + +#### func [IsAlpha](#isalpha) + +```go +func IsAlpha(s string) bool +``` +IsAlpha returns true if a string contains only letters from ASCII (a-z,A-Z). +Other letters from other languages are not supported. + +#### func [IsAlphaNumeric](#isalphanumeric) + +```go +func IsAlphaNumeric(s string) bool +``` +IsAlphaNumeric returns true if a string contains letters and digits. + +#### func [IsEmpty](#isempty) + +```go +func IsEmpty(s string) bool +``` +IsEmpty returns true if the string is solely composed of whitespace. + +#### func [IsLower](#islower) + +```go +func IsLower(s string) bool +``` +IsLower returns true if s comprised of all lower case characters. + +#### func [IsNumeric](#isnumeric) + +```go +func IsNumeric(s string) bool +``` +IsNumeric returns true if a string contains only digits from 0-9. Other digits +not in Latin (such as Arabic) are not currently supported. + +#### func [IsUpper](#isupper) + +```go +func IsUpper(s string) bool +``` +IsUpper returns true if s contains all upper case chracters. + +#### func [Left](#left) + +```go +func Left(s string, n int) string +``` +Left returns the left substring of length n. + +#### func [LeftF](#leftf) + +```go +func LeftF(n int) func(string) string +``` +LeftF is the filter form of Left. + +#### func [LeftOf](#leftof) + +```go +func LeftOf(s string, needle string) string +``` +LeftOf returns the substring left of needle. + +#### func [Letters](#letters) + +```go +func Letters(s string) []string +``` +Letters returns an array of runes as strings so it can be indexed into. + +#### func [Lines](#lines) + +```go +func Lines(s string) []string +``` +Lines convert windows newlines to unix newlines then convert to an Array of +lines. + +#### func [Map](#map) + +```go +func Map(arr []string, iterator func(string) string) []string +``` +Map maps an array's iitem through an iterator. + +#### func [Match](#match) + +```go +func Match(s, pattern string) bool +``` +Match returns true if patterns matches the string + +#### func [Pad](#pad) + +```go +func Pad(s, c string, n int) string +``` +Pad pads string s on both sides with c until it has length of n. + +#### func [PadF](#padf) + +```go +func PadF(c string, n int) func(string) string +``` +PadF is the filter form of Pad. + +#### func [PadLeft](#padleft) + +```go +func PadLeft(s, c string, n int) string +``` +PadLeft pads s on left side with c until it has length of n. + +#### func [PadLeftF](#padleftf) + +```go +func PadLeftF(c string, n int) func(string) string +``` +PadLeftF is the filter form of PadLeft. + +#### func [PadRight](#padright) + +```go +func PadRight(s, c string, n int) string +``` +PadRight pads s on right side with c until it has length of n. + +#### func [PadRightF](#padrightf) + +```go +func PadRightF(c string, n int) func(string) string +``` +PadRightF is the filter form of Padright + +#### func [Pipe](#pipe) + +```go +func Pipe(s string, funcs ...func(string) string) string +``` +Pipe pipes s through one or more string filters. + +#### func [QuoteItems](#quoteitems) + +```go +func QuoteItems(arr []string) []string +``` +QuoteItems quotes all items in array, mostly for debugging. + +#### func [ReplaceF](#replacef) + +```go +func ReplaceF(old, new string, n int) func(string) string +``` +ReplaceF is the filter form of strings.Replace. + +#### func [ReplacePattern](#replacepattern) + +```go +func ReplacePattern(s, pattern, repl string) string +``` +ReplacePattern replaces string with regexp string. ReplacePattern returns a copy +of src, replacing matches of the Regexp with the replacement string repl. Inside +repl, $ signs are interpreted as in Expand, so for instance $1 represents the +text of the first submatch. + +#### func [ReplacePatternF](#replacepatternf) + +```go +func ReplacePatternF(pattern, repl string) func(string) string +``` +ReplacePatternF is the filter form of ReplaceRegexp. + +#### func [Reverse](#reverse) + +```go +func Reverse(s string) string +``` +Reverse a string + +#### func [Right](#right) + +```go +func Right(s string, n int) string +``` +Right returns the right substring of length n. + +#### func [RightF](#rightf) + +```go +func RightF(n int) func(string) string +``` +RightF is the Filter version of Right. + +#### func [RightOf](#rightof) + +```go +func RightOf(s string, prefix string) string +``` +RightOf returns the substring to the right of prefix. + +#### func [SetTemplateDelimiters](#settemplatedelimiters) + +```go +func SetTemplateDelimiters(opening, closing string) +``` +SetTemplateDelimiters sets the delimiters for Template function. Defaults to +"{{" and "}}" + +#### func [Slice](#slice) + +```go +func Slice(s string, start, end int) string +``` +Slice slices a string. If end is negative then it is the from the end of the +string. + +#### func [SliceContains](#slicecontains) + +```go +func SliceContains(slice []string, val string) bool +``` +SliceContains determines whether val is an element in slice. + +#### func [SliceF](#slicef) + +```go +func SliceF(start, end int) func(string) string +``` +SliceF is the filter for Slice. + +#### func [SliceIndexOf](#sliceindexof) + +```go +func SliceIndexOf(slice []string, val string) int +``` +SliceIndexOf gets the indx of val in slice. Returns -1 if not found. + +#### func [Slugify](#slugify) + +```go +func Slugify(s string) string +``` +Slugify converts s into a dasherized string suitable for URL segment. + +#### func [StripPunctuation](#strippunctuation) + +```go +func StripPunctuation(s string) string +``` +StripPunctuation strips puncation from string. + +#### func [StripTags](#striptags) + +```go +func StripTags(s string, tags ...string) string +``` +StripTags strips all of the html tags or tags specified by the parameters + +#### func [Substr](#substr) + +```go +func Substr(s string, index int, n int) string +``` +Substr returns a substring of s starting at index of length n. + +#### func [SubstrF](#substrf) + +```go +func SubstrF(index, n int) func(string) string +``` +SubstrF is the filter form of Substr. + +#### func [Template](#template) + +```go +func Template(s string, values map[string]interface{}) string +``` +Template is a string template which replaces template placeholders delimited by +"{{" and "}}" with values from map. The global delimiters may be set with +SetTemplateDelimiters. + +#### func [TemplateDelimiters](#templatedelimiters) + +```go +func TemplateDelimiters() (opening string, closing string) +``` +TemplateDelimiters is the getter for the opening and closing delimiters for +Template. + +#### func [TemplateWithDelimiters](#templatewithdelimiters) + +```go +func TemplateWithDelimiters(s string, values map[string]interface{}, opening, closing string) string +``` +TemplateWithDelimiters is string template with user-defineable opening and +closing delimiters. + +#### func [ToArgv](#toargv) + +```go +func ToArgv(s string) []string +``` +ToArgv converts string s into an argv for exec. + +#### func [ToBool](#tobool) + +```go +func ToBool(s string) bool +``` +ToBool fuzzily converts truthy values. + +#### func [ToBoolOr](#toboolor) + +```go +func ToBoolOr(s string, defaultValue bool) bool +``` +ToBoolOr parses s as a bool or returns defaultValue. + +#### func [ToFloat32Or](#tofloat32or) + +```go +func ToFloat32Or(s string, defaultValue float32) float32 +``` +ToFloat32Or parses as a float32 or returns defaultValue on error. + +#### func [ToFloat64Or](#tofloat64or) + +```go +func ToFloat64Or(s string, defaultValue float64) float64 +``` +ToFloat64Or parses s as a float64 or returns defaultValue. + +#### func [ToIntOr](#tointor) + +```go +func ToIntOr(s string, defaultValue int) int +``` +ToIntOr parses s as an int or returns defaultValue. + +#### func [Underscore](#underscore) + +```go +func Underscore(s string) string +``` +Underscore returns converted camel cased string into a string delimited by +underscores. + +#### func [UnescapeHTML](#unescapehtml) + +```go +func UnescapeHTML(s string) string +``` +UnescapeHTML is an alias for html.UnescapeString. + +#### func [WrapHTML](#wraphtml) + +```go +func WrapHTML(s string, tag string, attrs map[string]string) string +``` +WrapHTML wraps s within HTML tag having attributes attrs. Note, WrapHTML does +not escape s value. + +#### func [WrapHTMLF](#wraphtmlf) + +```go +func WrapHTMLF(tag string, attrs map[string]string) func(string) string +``` +WrapHTMLF is the filter form of WrapHTML. diff --git a/vendor/github.com/mgutz/str/VERSION b/vendor/github.com/mgutz/str/VERSION new file mode 100644 index 000000000..9084fa2f7 --- /dev/null +++ b/vendor/github.com/mgutz/str/VERSION @@ -0,0 +1 @@ +1.1.0 diff --git a/vendor/github.com/mgutz/str/doc.go b/vendor/github.com/mgutz/str/doc.go new file mode 100644 index 000000000..f48742a1f --- /dev/null +++ b/vendor/github.com/mgutz/str/doc.go @@ -0,0 +1,19 @@ +// Package str is a comprehensive set of string functions to build more +// Go awesomeness. Str complements Go's standard packages and does not duplicate +// functionality found in `strings` or `strconv`. +// +// Str is based on plain functions instead of object-based methods, +// consistent with Go standard string packages. +// +// str.Between("foo", "", "") == "foo" +// +// Str supports pipelining instead of chaining +// +// s := str.Pipe("\nabcdef\n", Clean, BetweenF("a", "f"), ChompLeftF("bc")) +// +// User-defined filters can be added to the pipeline by inserting a function +// or closure that returns a function with this signature +// +// func(string) string +// +package str diff --git a/vendor/github.com/mgutz/str/funcsAO.go b/vendor/github.com/mgutz/str/funcsAO.go new file mode 100644 index 000000000..fd17c1c12 --- /dev/null +++ b/vendor/github.com/mgutz/str/funcsAO.go @@ -0,0 +1,337 @@ +package str + +import ( + "fmt" + "html" + //"log" + "regexp" + "strings" +) + +// Verbose flag enables console output for those functions that have +// counterparts in Go's excellent stadard packages. +var Verbose = false +var templateOpen = "{{" +var templateClose = "}}" + +var beginEndSpacesRe = regexp.MustCompile("^\\s+|\\s+$") +var camelizeRe = regexp.MustCompile(`(\-|_|\s)+(.)?`) +var camelizeRe2 = regexp.MustCompile(`(\-|_|\s)+`) +var capitalsRe = regexp.MustCompile("([A-Z])") +var dashSpaceRe = regexp.MustCompile(`[-\s]+`) +var dashesRe = regexp.MustCompile("-+") +var isAlphaNumericRe = regexp.MustCompile(`[^0-9a-z\xC0-\xFF]`) +var isAlphaRe = regexp.MustCompile(`[^a-z\xC0-\xFF]`) +var nWhitespaceRe = regexp.MustCompile(`\s+`) +var notDigitsRe = regexp.MustCompile(`[^0-9]`) +var slugifyRe = regexp.MustCompile(`[^\w\s\-]`) +var spaceUnderscoreRe = regexp.MustCompile("[_\\s]+") +var spacesRe = regexp.MustCompile("[\\s\\xA0]+") +var stripPuncRe = regexp.MustCompile(`[^\w\s]|_`) +var templateRe = regexp.MustCompile(`([\-\[\]()*\s])`) +var templateRe2 = regexp.MustCompile(`\$`) +var underscoreRe = regexp.MustCompile(`([a-z\d])([A-Z]+)`) +var whitespaceRe = regexp.MustCompile(`^[\s\xa0]*$`) + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// Between extracts a string between left and right strings. +func Between(s, left, right string) string { + l := len(left) + startPos := strings.Index(s, left) + if startPos < 0 { + return "" + } + endPos := IndexOf(s, right, startPos+l) + //log.Printf("%s: left %s right %s start %d end %d", s, left, right, startPos+l, endPos) + if endPos < 0 { + return "" + } else if right == "" { + return s[endPos:] + } else { + return s[startPos+l : endPos] + } +} + +// BetweenF is the filter form for Between. +func BetweenF(left, right string) func(string) string { + return func(s string) string { + return Between(s, left, right) + } +} + +// Camelize return new string which removes any underscores or dashes and convert a string into camel casing. +func Camelize(s string) string { + return camelizeRe.ReplaceAllStringFunc(s, func(val string) string { + val = strings.ToUpper(val) + val = camelizeRe2.ReplaceAllString(val, "") + return val + }) +} + +// Capitalize uppercases the first char of s and lowercases the rest. +func Capitalize(s string) string { + return strings.ToUpper(s[0:1]) + strings.ToLower(s[1:]) +} + +// CharAt returns a string from the character at the specified position. +func CharAt(s string, index int) string { + l := len(s) + shortcut := index < 0 || index > l-1 || l == 0 + if shortcut { + return "" + } + return s[index : index+1] +} + +// CharAtF is the filter form of CharAt. +func CharAtF(index int) func(string) string { + return func(s string) string { + return CharAt(s, index) + } +} + +// ChompLeft removes prefix at the start of a string. +func ChompLeft(s, prefix string) string { + if strings.HasPrefix(s, prefix) { + return s[len(prefix):] + } + return s +} + +// ChompLeftF is the filter form of ChompLeft. +func ChompLeftF(prefix string) func(string) string { + return func(s string) string { + return ChompLeft(s, prefix) + } +} + +// ChompRight removes suffix from end of s. +func ChompRight(s, suffix string) string { + if strings.HasSuffix(s, suffix) { + return s[:len(s)-len(suffix)] + } + return s +} + +// ChompRightF is the filter form of ChompRight. +func ChompRightF(suffix string) func(string) string { + return func(s string) string { + return ChompRight(s, suffix) + } +} + +// Classify returns a camelized string with the first letter upper cased. +func Classify(s string) string { + return Camelize("-" + s) +} + +// ClassifyF is the filter form of Classify. +func ClassifyF(s string) func(string) string { + return func(s string) string { + return Classify(s) + } +} + +// Clean compresses all adjacent whitespace to a single space and trims s. +func Clean(s string) string { + s = spacesRe.ReplaceAllString(s, " ") + s = beginEndSpacesRe.ReplaceAllString(s, "") + return s +} + +// Dasherize converts a camel cased string into a string delimited by dashes. +func Dasherize(s string) string { + s = strings.TrimSpace(s) + s = spaceUnderscoreRe.ReplaceAllString(s, "-") + s = capitalsRe.ReplaceAllString(s, "-$1") + s = dashesRe.ReplaceAllString(s, "-") + s = strings.ToLower(s) + return s +} + +// EscapeHTML is alias for html.EscapeString. +func EscapeHTML(s string) string { + if Verbose { + fmt.Println("Use html.EscapeString instead of EscapeHTML") + } + return html.EscapeString(s) +} + +// DecodeHTMLEntities decodes HTML entities into their proper string representation. +// DecodeHTMLEntities is an alias for html.UnescapeString +func DecodeHTMLEntities(s string) string { + if Verbose { + fmt.Println("Use html.UnescapeString instead of DecodeHTMLEntities") + } + return html.UnescapeString(s) +} + +// EnsurePrefix ensures s starts with prefix. +func EnsurePrefix(s, prefix string) string { + if strings.HasPrefix(s, prefix) { + return s + } + return prefix + s +} + +// EnsurePrefixF is the filter form of EnsurePrefix. +func EnsurePrefixF(prefix string) func(string) string { + return func(s string) string { + return EnsurePrefix(s, prefix) + } +} + +// EnsureSuffix ensures s ends with suffix. +func EnsureSuffix(s, suffix string) string { + if strings.HasSuffix(s, suffix) { + return s + } + return s + suffix +} + +// EnsureSuffixF is the filter form of EnsureSuffix. +func EnsureSuffixF(suffix string) func(string) string { + return func(s string) string { + return EnsureSuffix(s, suffix) + } +} + +// Humanize transforms s into a human friendly form. +func Humanize(s string) string { + if s == "" { + return s + } + s = Underscore(s) + var humanizeRe = regexp.MustCompile(`_id$`) + s = humanizeRe.ReplaceAllString(s, "") + s = strings.Replace(s, "_", " ", -1) + s = strings.TrimSpace(s) + s = Capitalize(s) + return s +} + +// Iif is short for immediate if. If condition is true return truthy else falsey. +func Iif(condition bool, truthy string, falsey string) string { + if condition { + return truthy + } + return falsey +} + +// IndexOf finds the index of needle in s starting from start. +func IndexOf(s string, needle string, start int) int { + l := len(s) + if needle == "" { + if start < 0 { + return 0 + } else if start < l { + return start + } else { + return l + } + } + if start < 0 || start > l-1 { + return -1 + } + pos := strings.Index(s[start:], needle) + if pos == -1 { + return -1 + } + return start + pos +} + +// IsAlpha returns true if a string contains only letters from ASCII (a-z,A-Z). Other letters from other languages are not supported. +func IsAlpha(s string) bool { + return !isAlphaRe.MatchString(strings.ToLower(s)) +} + +// IsAlphaNumeric returns true if a string contains letters and digits. +func IsAlphaNumeric(s string) bool { + return !isAlphaNumericRe.MatchString(strings.ToLower(s)) +} + +// IsLower returns true if s comprised of all lower case characters. +func IsLower(s string) bool { + return IsAlpha(s) && s == strings.ToLower(s) +} + +// IsNumeric returns true if a string contains only digits from 0-9. Other digits not in Latin (such as Arabic) are not currently supported. +func IsNumeric(s string) bool { + return !notDigitsRe.MatchString(s) +} + +// IsUpper returns true if s contains all upper case chracters. +func IsUpper(s string) bool { + return IsAlpha(s) && s == strings.ToUpper(s) +} + +// IsEmpty returns true if the string is solely composed of whitespace. +func IsEmpty(s string) bool { + if s == "" { + return true + } + return whitespaceRe.MatchString(s) +} + +// Left returns the left substring of length n. +func Left(s string, n int) string { + if n < 0 { + return Right(s, -n) + } + return Substr(s, 0, n) +} + +// LeftF is the filter form of Left. +func LeftF(n int) func(string) string { + return func(s string) string { + return Left(s, n) + } +} + +// LeftOf returns the substring left of needle. +func LeftOf(s string, needle string) string { + return Between(s, "", needle) +} + +// Letters returns an array of runes as strings so it can be indexed into. +func Letters(s string) []string { + result := []string{} + for _, r := range s { + result = append(result, string(r)) + } + return result +} + +// Lines convert windows newlines to unix newlines then convert to an Array of lines. +func Lines(s string) []string { + s = strings.Replace(s, "\r\n", "\n", -1) + return strings.Split(s, "\n") +} + +// Map maps an array's iitem through an iterator. +func Map(arr []string, iterator func(string) string) []string { + r := []string{} + for _, item := range arr { + r = append(r, iterator(item)) + } + return r +} + +// Match returns true if patterns matches the string +func Match(s, pattern string) bool { + r := regexp.MustCompile(pattern) + return r.MatchString(s) +} diff --git a/vendor/github.com/mgutz/str/funcsPZ.go b/vendor/github.com/mgutz/str/funcsPZ.go new file mode 100644 index 000000000..e8fe43f21 --- /dev/null +++ b/vendor/github.com/mgutz/str/funcsPZ.go @@ -0,0 +1,534 @@ +package str + +import ( + "fmt" + "html" + //"log" + "math" + "regexp" + "runtime" + "strconv" + "strings" + "unicode/utf8" +) + +// Pad pads string s on both sides with c until it has length of n. +func Pad(s, c string, n int) string { + L := len(s) + if L >= n { + return s + } + n -= L + + left := strings.Repeat(c, int(math.Ceil(float64(n)/2))) + right := strings.Repeat(c, int(math.Floor(float64(n)/2))) + return left + s + right +} + +// PadF is the filter form of Pad. +func PadF(c string, n int) func(string) string { + return func(s string) string { + return Pad(s, c, n) + } +} + +// PadLeft pads s on left side with c until it has length of n. +func PadLeft(s, c string, n int) string { + L := len(s) + if L > n { + return s + } + return strings.Repeat(c, (n-L)) + s +} + +// PadLeftF is the filter form of PadLeft. +func PadLeftF(c string, n int) func(string) string { + return func(s string) string { + return PadLeft(s, c, n) + } +} + +// PadRight pads s on right side with c until it has length of n. +func PadRight(s, c string, n int) string { + L := len(s) + if L > n { + return s + } + return s + strings.Repeat(c, n-L) +} + +// PadRightF is the filter form of Padright +func PadRightF(c string, n int) func(string) string { + return func(s string) string { + return PadRight(s, c, n) + } +} + +// Pipe pipes s through one or more string filters. +func Pipe(s string, funcs ...func(string) string) string { + for _, fn := range funcs { + s = fn(s) + } + return s +} + +// QuoteItems quotes all items in array, mostly for debugging. +func QuoteItems(arr []string) []string { + return Map(arr, func(s string) string { + return strconv.Quote(s) + }) +} + +// ReplaceF is the filter form of strings.Replace. +func ReplaceF(old, new string, n int) func(string) string { + return func(s string) string { + return strings.Replace(s, old, new, n) + } +} + +// ReplacePattern replaces string with regexp string. +// ReplacePattern returns a copy of src, replacing matches of the Regexp with the replacement string repl. Inside repl, $ signs are interpreted as in Expand, so for instance $1 represents the text of the first submatch. +func ReplacePattern(s, pattern, repl string) string { + r := regexp.MustCompile(pattern) + return r.ReplaceAllString(s, repl) +} + +// ReplacePatternF is the filter form of ReplaceRegexp. +func ReplacePatternF(pattern, repl string) func(string) string { + return func(s string) string { + return ReplacePattern(s, pattern, repl) + } +} + +// Reverse a string +func Reverse(s string) string { + cs := make([]rune, utf8.RuneCountInString(s)) + i := len(cs) + for _, c := range s { + i-- + cs[i] = c + } + return string(cs) +} + +// Right returns the right substring of length n. +func Right(s string, n int) string { + if n < 0 { + return Left(s, -n) + } + return Substr(s, len(s)-n, n) +} + +// RightF is the Filter version of Right. +func RightF(n int) func(string) string { + return func(s string) string { + return Right(s, n) + } +} + +// RightOf returns the substring to the right of prefix. +func RightOf(s string, prefix string) string { + return Between(s, prefix, "") +} + +// SetTemplateDelimiters sets the delimiters for Template function. Defaults to "{{" and "}}" +func SetTemplateDelimiters(opening, closing string) { + templateOpen = opening + templateClose = closing +} + +// Slice slices a string. If end is negative then it is the from the end +// of the string. +func Slice(s string, start, end int) string { + if end > -1 { + return s[start:end] + } + L := len(s) + if L+end > 0 { + return s[start : L-end] + } + return s[start:] +} + +// SliceF is the filter for Slice. +func SliceF(start, end int) func(string) string { + return func(s string) string { + return Slice(s, start, end) + } +} + +// SliceContains determines whether val is an element in slice. +func SliceContains(slice []string, val string) bool { + if slice == nil { + return false + } + + for _, it := range slice { + if it == val { + return true + } + } + return false +} + +// SliceIndexOf gets the indx of val in slice. Returns -1 if not found. +func SliceIndexOf(slice []string, val string) int { + if slice == nil { + return -1 + } + + for i, it := range slice { + if it == val { + return i + } + } + return -1 +} + +// Slugify converts s into a dasherized string suitable for URL segment. +func Slugify(s string) string { + sl := slugifyRe.ReplaceAllString(s, "") + sl = strings.ToLower(sl) + sl = Dasherize(sl) + return sl +} + +// StripPunctuation strips puncation from string. +func StripPunctuation(s string) string { + s = stripPuncRe.ReplaceAllString(s, "") + s = nWhitespaceRe.ReplaceAllString(s, " ") + return s +} + +// StripTags strips all of the html tags or tags specified by the parameters +func StripTags(s string, tags ...string) string { + if len(tags) == 0 { + tags = append(tags, "") + } + for _, tag := range tags { + stripTagsRe := regexp.MustCompile(`(?i)<\/?` + tag + `[^<>]*>`) + s = stripTagsRe.ReplaceAllString(s, "") + } + return s +} + +// Substr returns a substring of s starting at index of length n. +func Substr(s string, index int, n int) string { + L := len(s) + if index < 0 || index >= L || s == "" { + return "" + } + end := index + n + if end >= L { + end = L + } + if end <= index { + return "" + } + return s[index:end] +} + +// SubstrF is the filter form of Substr. +func SubstrF(index, n int) func(string) string { + return func(s string) string { + return Substr(s, index, n) + } +} + +// Template is a string template which replaces template placeholders delimited +// by "{{" and "}}" with values from map. The global delimiters may be set with +// SetTemplateDelimiters. +func Template(s string, values map[string]interface{}) string { + return TemplateWithDelimiters(s, values, templateOpen, templateClose) +} + +// TemplateDelimiters is the getter for the opening and closing delimiters for Template. +func TemplateDelimiters() (opening string, closing string) { + return templateOpen, templateClose +} + +// TemplateWithDelimiters is string template with user-defineable opening and closing delimiters. +func TemplateWithDelimiters(s string, values map[string]interface{}, opening, closing string) string { + escapeDelimiter := func(delim string) string { + result := templateRe.ReplaceAllString(delim, "\\$1") + return templateRe2.ReplaceAllString(result, "\\$") + } + + openingDelim := escapeDelimiter(opening) + closingDelim := escapeDelimiter(closing) + r := regexp.MustCompile(openingDelim + `(.+?)` + closingDelim) + matches := r.FindAllStringSubmatch(s, -1) + for _, submatches := range matches { + match := submatches[0] + key := submatches[1] + //log.Printf("match %s key %s\n", match, key) + if values[key] != nil { + v := fmt.Sprintf("%v", values[key]) + s = strings.Replace(s, match, v, -1) + } + } + + return s +} + +// ToArgv converts string s into an argv for exec. +func ToArgv(s string) []string { + const ( + InArg = iota + InArgQuote + OutOfArg + ) + currentState := OutOfArg + currentQuoteChar := "\x00" // to distinguish between ' and " quotations + // this allows to use "foo'bar" + currentArg := "" + argv := []string{} + + isQuote := func(c string) bool { + return c == `"` || c == `'` + } + + isEscape := func(c string) bool { + return c == `\` + } + + isWhitespace := func(c string) bool { + return c == " " || c == "\t" + } + + L := len(s) + for i := 0; i < L; i++ { + c := s[i : i+1] + + //fmt.Printf("c %s state %v arg %s argv %v i %d\n", c, currentState, currentArg, args, i) + if isQuote(c) { + switch currentState { + case OutOfArg: + currentArg = "" + fallthrough + case InArg: + currentState = InArgQuote + currentQuoteChar = c + + case InArgQuote: + if c == currentQuoteChar { + currentState = InArg + } else { + currentArg += c + } + } + + } else if isWhitespace(c) { + switch currentState { + case InArg: + argv = append(argv, currentArg) + currentState = OutOfArg + case InArgQuote: + currentArg += c + case OutOfArg: + // nothing + } + + } else if isEscape(c) { + switch currentState { + case OutOfArg: + currentArg = "" + currentState = InArg + fallthrough + case InArg: + fallthrough + case InArgQuote: + if i == L-1 { + if runtime.GOOS == "windows" { + // just add \ to end for windows + currentArg += c + } else { + panic("Escape character at end string") + } + } else { + if runtime.GOOS == "windows" { + peek := s[i+1 : i+2] + if peek != `"` { + currentArg += c + } + } else { + i++ + c = s[i : i+1] + currentArg += c + } + } + } + } else { + switch currentState { + case InArg, InArgQuote: + currentArg += c + + case OutOfArg: + currentArg = "" + currentArg += c + currentState = InArg + } + } + } + + if currentState == InArg { + argv = append(argv, currentArg) + } else if currentState == InArgQuote { + panic("Starting quote has no ending quote.") + } + + return argv +} + +// ToBool fuzzily converts truthy values. +func ToBool(s string) bool { + s = strings.ToLower(s) + return s == "true" || s == "yes" || s == "on" || s == "1" +} + +// ToBoolOr parses s as a bool or returns defaultValue. +func ToBoolOr(s string, defaultValue bool) bool { + b, err := strconv.ParseBool(s) + if err != nil { + return defaultValue + } + return b +} + +// ToIntOr parses s as an int or returns defaultValue. +func ToIntOr(s string, defaultValue int) int { + n, err := strconv.Atoi(s) + if err != nil { + return defaultValue + } + return n +} + +// ToFloat32Or parses as a float32 or returns defaultValue on error. +func ToFloat32Or(s string, defaultValue float32) float32 { + f, err := strconv.ParseFloat(s, 32) + if err != nil { + return defaultValue + } + return float32(f) +} + +// ToFloat64Or parses s as a float64 or returns defaultValue. +func ToFloat64Or(s string, defaultValue float64) float64 { + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return defaultValue + } + return f +} + +// ToFloatOr parses as a float64 or returns defaultValue. +var ToFloatOr = ToFloat64Or + +// TODO This is not working yet. Go's regexp package does not have some +// of the niceities in JavaScript +// +// Truncate truncates the string, accounting for word placement and chars count +// adding a morestr (defaults to ellipsis) +// func Truncate(s, morestr string, n int) string { +// L := len(s) +// if L <= n { +// return s +// } +// +// if morestr == "" { +// morestr = "..." +// } +// +// tmpl := func(c string) string { +// if strings.ToUpper(c) != strings.ToLower(c) { +// return "A" +// } +// return " " +// } +// template := s[0 : n+1] +// var truncateRe = regexp.MustCompile(`.(?=\W*\w*$)`) +// truncateRe.ReplaceAllStringFunc(template, tmpl) // 'Hello, world' -> 'HellAA AAAAA' +// var wwRe = regexp.MustCompile(`\w\w`) +// var whitespaceRe2 = regexp.MustCompile(`\s*\S+$`) +// if wwRe.MatchString(template[len(template)-2:]) { +// template = whitespaceRe2.ReplaceAllString(template, "") +// } else { +// template = strings.TrimRight(template, " \t\n") +// } +// +// if len(template+morestr) > L { +// return s +// } +// return s[0:len(template)] + morestr +// } +// +// truncate: function(length, pruneStr) { //from underscore.string, author: github.com/rwz +// var str = this.s; +// +// length = ~~length; +// pruneStr = pruneStr || '...'; +// +// if (str.length <= length) return new this.constructor(str); +// +// var tmpl = function(c){ return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; }, +// template = str.slice(0, length+1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA' +// +// if (template.slice(template.length-2).match(/\w\w/)) +// template = template.replace(/\s*\S+$/, ''); +// else +// template = new S(template.slice(0, template.length-1)).trimRight().s; +// +// return (template+pruneStr).length > str.length ? new S(str) : new S(str.slice(0, template.length)+pruneStr); +// }, + +// Underscore returns converted camel cased string into a string delimited by underscores. +func Underscore(s string) string { + if s == "" { + return "" + } + u := strings.TrimSpace(s) + + u = underscoreRe.ReplaceAllString(u, "${1}_$2") + u = dashSpaceRe.ReplaceAllString(u, "_") + u = strings.ToLower(u) + if IsUpper(s[0:1]) { + return "_" + u + } + return u +} + +// UnescapeHTML is an alias for html.UnescapeString. +func UnescapeHTML(s string) string { + if Verbose { + fmt.Println("Use html.UnescapeString instead of UnescapeHTML") + } + return html.UnescapeString(s) +} + +// WrapHTML wraps s within HTML tag having attributes attrs. Note, +// WrapHTML does not escape s value. +func WrapHTML(s string, tag string, attrs map[string]string) string { + escapeHTMLAttributeQuotes := func(v string) string { + v = strings.Replace(v, "<", "<", -1) + v = strings.Replace(v, "&", "&", -1) + v = strings.Replace(v, "\"", """, -1) + return v + } + if tag == "" { + tag = "div" + } + el := "<" + tag + for name, val := range attrs { + el += " " + name + "=\"" + escapeHTMLAttributeQuotes(val) + "\"" + } + el += ">" + s + "" + return el +} + +// WrapHTMLF is the filter form of WrapHTML. +func WrapHTMLF(tag string, attrs map[string]string) func(string) string { + return func(s string) string { + return WrapHTML(s, tag, attrs) + } +} diff --git a/vendor/github.com/mgutz/str/str_test.go b/vendor/github.com/mgutz/str/str_test.go new file mode 100644 index 000000000..3186a387d --- /dev/null +++ b/vendor/github.com/mgutz/str/str_test.go @@ -0,0 +1,696 @@ +package str + +//import "testing" +import "fmt" + +//import "strings" + +func ExampleBetween() { + eg(1, Between("foo", "", "")) + eg(2, Between("foo", "", "")) + eg(3, Between("foo", "", "")) + eg(4, Between("foo", "", "")) + eg(5, Between("foo", "", "")) + eg(6, Between("Some strings } are very {weird}, dont you think?", "{", "}")) + eg(7, Between("This is ateststring", "", "test")) + eg(8, Between("This is ateststring", "test", "")) + // Output: + // 1: foo + // 2: foo + // 3: foo + // 4: foo + // 5: + // 6: weird + // 7: This is a + // 8: string +} + +func ExampleBetweenF() { + eg(1, Pipe("abc", BetweenF("a", "c"))) + // Output: + // 1: b +} + +func ExampleCamelize() { + eg(1, Camelize("data_rate")) + eg(2, Camelize("background-color")) + eg(3, Camelize("-moz-something")) + eg(4, Camelize("_car_speed_")) + eg(5, Camelize("yes_we_can")) + // Output: + // 1: dataRate + // 2: backgroundColor + // 3: MozSomething + // 4: CarSpeed + // 5: yesWeCan +} + +func ExampleCapitalize() { + eg(1, Capitalize("abc")) + eg(2, Capitalize("ABC")) + // Output: + // 1: Abc + // 2: Abc +} + +func ExampleCharAt() { + eg(1, CharAt("abc", 1)) + eg(2, CharAt("", -1)) + eg(3, CharAt("", 0)) + eg(4, CharAt("", 10)) + eg(5, CharAt("abc", -1)) + eg(6, CharAt("abc", 10)) + // Output: + // 1: b + // 2: + // 3: + // 4: + // 5: + // 6: +} + +func ExampleCharAtF() { + eg(1, Pipe("abc", CharAtF(1))) + // Output: + // 1: b +} + +func ExampleChompLeft() { + eg(1, ChompLeft("foobar", "foo")) + eg(2, ChompLeft("foobar", "bar")) + eg(3, ChompLeft("", "foo")) + eg(4, ChompLeft("", "")) + eg(5, ChompLeft("foo", "")) + // Output: + // 1: bar + // 2: foobar + // 3: + // 4: + // 5: foo +} + +func ExampleChompLeftF() { + eg(1, Pipe("abc", ChompLeftF("ab"))) + // Output: + // 1: c +} + +func ExampleChompRight() { + eg(1, ChompRight("foobar", "foo")) + eg(2, ChompRight("foobar", "bar")) + eg(3, ChompRight("", "foo")) + eg(4, ChompRight("", "")) + // Output: + // 1: foobar + // 2: foo + // 3: + // 4: +} + +func ExampleChompRightF() { + eg(1, Pipe("abc", ChompRightF("bc"))) + // Output: + // 1: a +} + +func ExampleClassify() { + eg(1, Classify("data_rate")) + eg(2, Classify("background-color")) + eg(3, Classify("-moz-something")) + eg(4, Classify("_car_speed_")) + eg(5, Classify("yes_we_can")) + // Output: + // 1: DataRate + // 2: BackgroundColor + // 3: MozSomething + // 4: CarSpeed + // 5: YesWeCan +} + +func ExampleClean() { + eg(1, Clean("clean")) + eg(2, Clean("")) + eg(3, Clean(" please\t clean \t \n me ")) + // Output: + // 1: clean + // 2: + // 3: please clean me +} + +func ExampleDasherize() { + eg(1, Dasherize("dataRate")) + eg(2, Dasherize("CarSpeed")) + eg(3, Dasherize("yesWeCan")) + eg(4, Dasherize("")) + eg(5, Dasherize("ABC")) + // Output: + // 1: data-rate + // 2: -car-speed + // 3: yes-we-can + // 4: + // 5: -a-b-c +} + +func ExampleDecodeHTMLEntities() { + eg(1, DecodeHTMLEntities("Ken Thompson & Dennis Ritchie")) + eg(2, DecodeHTMLEntities("3 < 4")) + eg(3, DecodeHTMLEntities("http://")) + // Output: + // 1: Ken Thompson & Dennis Ritchie + // 2: 3 < 4 + // 3: http:// +} + +func ExampleEnsurePrefix() { + eg(1, EnsurePrefix("foobar", "foo")) + eg(2, EnsurePrefix("bar", "foo")) + eg(3, EnsurePrefix("", "")) + eg(4, EnsurePrefix("foo", "")) + eg(5, EnsurePrefix("", "foo")) + // Output: + // 1: foobar + // 2: foobar + // 3: + // 4: foo + // 5: foo +} + +func ExampleEnsurePrefixF() { + eg(1, Pipe("dir", EnsurePrefixF("./"))) + // Output: + // 1: ./dir +} + +func ExampleEnsureSuffix() { + eg(1, EnsureSuffix("foobar", "bar")) + eg(2, EnsureSuffix("foo", "bar")) + eg(3, EnsureSuffix("", "")) + eg(4, EnsureSuffix("foo", "")) + eg(5, EnsureSuffix("", "bar")) + // Output: + // 1: foobar + // 2: foobar + // 3: + // 4: foo + // 5: bar +} + +func ExampleHumanize() { + eg(1, Humanize("the_humanize_string_method")) + eg(2, Humanize("ThehumanizeStringMethod")) + eg(3, Humanize("the humanize string method")) + // Output: + // 1: The humanize string method + // 2: Thehumanize string method + // 3: The humanize string method +} + +func ExampleIif() { + eg(1, Iif(true, "T", "F")) + eg(2, Iif(false, "T", "F")) + // Output: + // 1: T + // 2: F +} + +func ExampleIndexOf() { + eg(1, IndexOf("abcdef", "a", 0)) + eg(2, IndexOf("abcdef", "a", 3)) + eg(3, IndexOf("abcdef", "a", -2)) + eg(4, IndexOf("abcdef", "a", 10)) + eg(5, IndexOf("", "a", 0)) + eg(6, IndexOf("abcdef", "", 2)) + eg(7, IndexOf("abcdef", "", 1000)) + // Output: + // 1: 0 + // 2: -1 + // 3: -1 + // 4: -1 + // 5: -1 + // 6: 2 + // 7: 6 +} + +func ExampleIsAlpha() { + eg(1, IsAlpha("afaf")) + eg(2, IsAlpha("FJslfjkasfs")) + eg(3, IsAlpha("áéúóúÁÉÍÓÚãõÃÕàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜçÇ")) + eg(4, IsAlpha("adflj43faljsdf")) + eg(5, IsAlpha("33")) + eg(6, IsAlpha("TT....TTTafafetstYY")) + eg(7, IsAlpha("-áéúóúÁÉÍÓÚãõÃÕàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜçÇ")) + // Output: + // 1: true + // 2: true + // 3: true + // 4: false + // 5: false + // 6: false + // 7: false +} + +func eg(index int, example interface{}) { + output := fmt.Sprintf("%d: %v", index, example) + fmt.Printf("%s\n", Clean(output)) +} + +func ExampleIsAlphaNumeric() { + eg(1, IsAlphaNumeric("afaf35353afaf")) + eg(2, IsAlphaNumeric("FFFF99fff")) + eg(3, IsAlphaNumeric("99")) + eg(4, IsAlphaNumeric("afff")) + eg(5, IsAlphaNumeric("Infinity")) + eg(6, IsAlphaNumeric("áéúóúÁÉÍÓÚãõÃÕàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜçÇ1234567890")) + eg(7, IsAlphaNumeric("-Infinity")) + eg(8, IsAlphaNumeric("-33")) + eg(9, IsAlphaNumeric("aaff..")) + eg(10, IsAlphaNumeric(".áéúóúÁÉÍÓÚãõÃÕàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜçÇ1234567890")) + // Output: + // 1: true + // 2: true + // 3: true + // 4: true + // 5: true + // 6: true + // 7: false + // 8: false + // 9: false + // 10: false +} + +func ExampleIsEmpty() { + eg(1, IsEmpty(" ")) + eg(2, IsEmpty("\t\t\t ")) + eg(3, IsEmpty("\t\n ")) + eg(4, IsEmpty("hi")) + // Output: + // 1: true + // 2: true + // 3: true + // 4: false +} + +func ExampleIsLower() { + eg(1, IsLower("a")) + eg(2, IsLower("A")) + eg(3, IsLower("abc")) + eg(4, IsLower("aBc")) + eg(5, IsLower("áéúóúãõàèìòùâêîôûäëïöüç")) + eg(6, IsLower("hi jp")) + eg(7, IsLower("ÁÉÍÓÚÃÕÀÈÌÒÙÂÊÎÔÛÄËÏÖÜÇ")) + eg(8, IsLower("áéúóúãõàèìòùâêîôûäëïöüçÁ")) + eg(9, IsLower("áéúóúãõàèìòùâêîôû äëïöüç")) + // Output: + // 1: true + // 2: false + // 3: true + // 4: false + // 5: true + // 6: false + // 7: false + // 8: false + // 9: false +} + +func ExampleIsNumeric() { + eg(1, IsNumeric("3")) + eg(2, IsNumeric("34.22")) + eg(3, IsNumeric("-22.33")) + eg(4, IsNumeric("NaN")) + eg(5, IsNumeric("Infinity")) + eg(6, IsNumeric("-Infinity")) + eg(7, IsNumeric("JP")) + eg(8, IsNumeric("-5")) + eg(9, IsNumeric("00099242424")) + // Output: + // 1: true + // 2: false + // 3: false + // 4: false + // 5: false + // 6: false + // 7: false + // 8: false + // 9: true +} + +func ExampleIsUpper() { + eg(1, IsUpper("a")) + eg(2, IsUpper("A")) + eg(3, IsUpper("ABC")) + eg(4, IsUpper("aBc")) + eg(5, IsUpper("áéúóúãõàèìòùâêîôûäëïöüç")) + eg(6, IsUpper("HI JP")) + eg(7, IsUpper("ÁÉÍÓÚÃÕÀÈÌÒÙÂÊÎÔÛÄËÏÖÜÇ")) + eg(8, IsUpper("áéúóúãõàèìòùâêîôûäëïöüçÁ")) + eg(9, IsUpper("ÁÉÍÓÚÃÕÀÈÌÒÙÂÊÎ ÔÛÄËÏÖÜÇ")) + // Output: + // 1: false + // 2: true + // 3: true + // 4: false + // 5: false + // 6: false + // 7: true + // 8: false + // 9: false +} + +func ExampleLeft() { + eg(1, Left("abcdef", 0)) + eg(2, Left("abcdef", 1)) + eg(3, Left("abcdef", 4)) + eg(4, Left("abcdef", -2)) + // Output: + // 1: + // 2: a + // 3: abcd + // 4: ef +} + +func ExampleLeftOf() { + eg(1, LeftOf("abcdef", "def")) + eg(2, LeftOf("abcdef", "abc")) + eg(3, LeftOf("abcdef", "")) + eg(4, LeftOf("", "abc")) + eg(5, LeftOf("abcdef", "xyz")) + // Output: + // 1: abc + // 2: + // 3: abcdef + // 4: + // 5: +} + +func ExampleLines() { + eg(1, Lines("a\r\nb\nc\r\n")) + eg(2, Lines("a\r\nb\nc\r\nd")) + // Output: + // 1: [a b c ] + // 2: [a b c d] +} + +func ExampleMatch() { + eg(1, Match("foobar", `^fo.*r$`)) + eg(2, Match("foobar", `^fo.*x$`)) + eg(3, Match("", `^fo.*x$`)) + // Output: + // 1: true + // 2: false + // 3: false +} + +func ExamplePad() { + eg(1, Pad("hello", "x", 5)) + eg(2, Pad("hello", "x", 10)) + eg(3, Pad("hello", "x", 11)) + eg(4, Pad("hello", "x", 6)) + eg(5, Pad("hello", "x", 1)) + // Output: + // 1: hello + // 2: xxxhelloxx + // 3: xxxhelloxxx + // 4: xhello + // 5: hello +} + +func ExamplePadLeft() { + eg(1, PadLeft("hello", "x", 5)) + eg(2, PadLeft("hello", "x", 10)) + eg(3, PadLeft("hello", "x", 11)) + eg(4, PadLeft("hello", "x", 6)) + eg(5, PadLeft("hello", "x", 1)) + // Output: + // 1: hello + // 2: xxxxxhello + // 3: xxxxxxhello + // 4: xhello + // 5: hello +} + +func ExamplePadRight() { + eg(1, PadRight("hello", "x", 5)) + eg(2, PadRight("hello", "x", 10)) + eg(3, PadRight("hello", "x", 11)) + eg(4, PadRight("hello", "x", 6)) + eg(5, PadRight("hello", "x", 1)) + // Output: + // 1: hello + // 2: helloxxxxx + // 3: helloxxxxxx + // 4: hellox + // 5: hello +} + +func ExamplePipe() { + eg(1, Pipe("\nabcdef \n", Clean, BetweenF("a", "f"), ChompLeftF("bc"))) + // Output: + // 1: de +} + +func ExampleReplaceF() { + eg(1, Pipe("abcdefab", ReplaceF("ab", "x", -1))) + eg(2, Pipe("abcdefab", ReplaceF("ab", "x", 1))) + eg(3, Pipe("abcdefab", ReplaceF("ab", "x", 0))) + // Output: + // 1: xcdefx + // 2: xcdefab + // 3: abcdefab +} + +func ExampleReplacePattern() { + eg(1, ReplacePattern("aabbcc", `a`, "x")) + // Output: + // 1: xxbbcc +} + +func ExampleReplacePatternF() { + eg(1, Pipe("aabbcc", ReplacePatternF(`a`, "x"))) + // Output: + // 1: xxbbcc +} + +func ExampleReverse() { + eg(1, Reverse("abc")) + eg(2, Reverse("中文")) + // Output: + // 1: cba + // 2: 文中 +} + +func ExampleRight() { + eg(1, Right("abcdef", 0)) + eg(2, Right("abcdef", 1)) + eg(3, Right("abcdef", 4)) + eg(4, Right("abcdef", -2)) + // Output: + // 1: + // 2: f + // 3: cdef + // 4: ab +} + +func ExampleRightOf() { + eg(1, RightOf("abcdef", "abc")) + eg(2, RightOf("abcdef", "def")) + eg(3, RightOf("abcdef", "")) + eg(4, RightOf("", "abc")) + eg(5, RightOf("abcdef", "xyz")) + // Output: + // 1: def + // 2: + // 3: abcdef + // 4: + // 5: +} + +func ExampleRightF() { + eg(1, Pipe("abcdef", RightF(3))) + // Output: + // 1: def +} + +func ExampleSliceContains() { + eg(1, SliceContains([]string{"foo", "bar"}, "foo")) + eg(2, SliceContains(nil, "foo")) + eg(3, SliceContains([]string{"foo", "bar"}, "bah")) + eg(4, SliceContains([]string{"foo", "bar"}, "")) + // Output: + // 1: true + // 2: false + // 3: false + // 4: false +} + +func ExampleSliceIndexOf() { + eg(1, SliceIndexOf([]string{"foo", "bar"}, "foo")) + eg(2, SliceIndexOf(nil, "foo")) + eg(3, SliceIndexOf([]string{"foo", "bar"}, "bah")) + eg(4, SliceIndexOf([]string{"foo", "bar"}, "")) + eg(5, SliceIndexOf([]string{"foo", "bar"}, "bar")) + // Output: + // 1: 0 + // 2: -1 + // 3: -1 + // 4: -1 + // 5: 1 +} + +func ExampleSlugify() { + eg(1, Slugify("foo bar")) + eg(2, Slugify("foo/bar bah")) + eg(3, Slugify("foo-bar--bah")) + // Output: + // 1: foo-bar + // 2: foobar-bah + // 3: foo-bar-bah +} + +func ExampleStripPunctuation() { + eg(1, StripPunctuation("My, st[ring] *full* of %punct)")) + // Output: + // 1: My string full of punct +} + +func ExampleStripTags() { + eg(1, StripTags("

just some text

")) + eg(2, StripTags("

just some text

", "p")) + eg(3, StripTags("

just some text

", "a", "p")) + eg(4, StripTags("

just some text

", "b")) + // Output: + // 1: just some text + // 2: just some text + // 3: just some text + // 4:

just some text

+} + +func ExampleSubstr() { + eg(1, Substr("abcdef", 2, -1)) + eg(2, Substr("abcdef", 2, 0)) + eg(3, Substr("abcdef", 2, 1)) + eg(4, Substr("abcdef", 2, 3)) + eg(5, Substr("abcdef", 2, 4)) + eg(6, Substr("abcdef", 2, 100)) + eg(7, Substr("abcdef", 0, 1)) + // Output: + // 1: + // 2: + // 3: c + // 4: cde + // 5: cdef + // 6: cdef + // 7: a +} + +func ExampleTemplateWithDelimiters() { + eg(1, TemplateWithDelimiters("Hello {{name}} at {{date-year}}", map[string]interface{}{"name": "foo", "date-year": 2014}, "{{", "}}")) + eg(2, TemplateWithDelimiters("Hello #{name} at #{date-year}", map[string]interface{}{"name": "foo", "date-year": 2014}, "#{", "}")) + eg(3, TemplateWithDelimiters("Hello (name) at (date-year)", map[string]interface{}{"name": "foo", "date-year": 2014}, "(", ")")) + eg(4, TemplateWithDelimiters("Hello [name] at [date-year]", map[string]interface{}{"name": "foo", "date-year": 2014}, "[", "]")) + eg(5, TemplateWithDelimiters("Hello *name* at *date-year*", map[string]interface{}{"name": "foo", "date-year": 2014}, "*", "*")) + eg(6, TemplateWithDelimiters("Hello $name$ at $date-year$", map[string]interface{}{"name": "foo", "date-year": 2014}, "$", "$")) + // Output: + // 1: Hello foo at 2014 + // 2: Hello foo at 2014 + // 3: Hello foo at 2014 + // 4: Hello foo at 2014 + // 5: Hello foo at 2014 + // 6: Hello foo at 2014 +} + +func ExampleTemplate() { + eg(1, Template("Hello {{name}} at {{date-year}}", map[string]interface{}{"name": "foo", "date-year": 2014})) + eg(2, Template("Hello {{name}}", map[string]interface{}{"name": ""})) + SetTemplateDelimiters("{", "}") + eg(3, Template("Hello {name} at {date-year}", map[string]interface{}{"name": "foo", "date-year": 2014})) + // Output: + // 1: Hello foo at 2014 + // 2: Hello + // 3: Hello foo at 2014 +} + +func ExampleToArgv() { + eg(1, QuoteItems(ToArgv(`GO_ENV=test gosu --watch foo@release "some quoted string 'inside'"`))) + eg(2, QuoteItems(ToArgv(`gosu foo\ bar`))) + eg(3, QuoteItems(ToArgv(`gosu --test="some arg" -w -s a=123`))) + // Output: + // 1: ["GO_ENV=test" "gosu" "--watch" "foo@release" "some quoted string 'inside'"] + // 2: ["gosu" "foo bar"] + // 3: ["gosu" "--test=some arg" "-w" "-s" "a=123"] +} + +func ExampleToBool() { + eg(1, ToBool("true")) + eg(2, ToBool("yes")) + eg(3, ToBool("1")) + eg(4, ToBool("on")) + eg(5, ToBool("false")) + eg(6, ToBool("no")) + eg(7, ToBool("0")) + eg(8, ToBool("off")) + eg(9, ToBool("")) + eg(10, ToBool("?")) + // Output: + // 1: true + // 2: true + // 3: true + // 4: true + // 5: false + // 6: false + // 7: false + // 8: false + // 9: false + // 10: false +} + +func ExampleToBoolOr() { + eg(1, ToBoolOr("foo", true)) + eg(2, ToBoolOr("foo", false)) + eg(3, ToBoolOr("true", false)) + eg(4, ToBoolOr("", true)) + // Output: + // 1: true + // 2: false + // 3: true + // 4: true +} + +func ExampleToIntOr() { + eg(1, ToIntOr("foo", 0)) + eg(2, ToIntOr("", 1)) + eg(3, ToIntOr("100", 0)) + eg(4, ToIntOr("-1", 1)) + // Output: + // 1: 0 + // 2: 1 + // 3: 100 + // 4: -1 +} + +func ExampleUnderscore() { + eg(1, Underscore("fooBar")) + eg(2, Underscore("FooBar")) + eg(3, Underscore("")) + eg(4, Underscore("x")) + // Output: + // 1: foo_bar + // 2: _foo_bar + // 3: + // 4: x +} + +func ExampleWrapHTML() { + eg(1, WrapHTML("foo", "span", nil)) + eg(2, WrapHTML("foo", "", nil)) + eg(3, WrapHTML("foo", "", map[string]string{"class": "bar"})) + // Output: + // 1: foo + // 2:
foo
+ // 3:
foo
+} + +func ExampleWrapHTMLF() { + eg(1, Pipe("foo", WrapHTMLF("div", nil))) + // Output: + // 1:
foo
+} diff --git a/vendor/vendor.json b/vendor/vendor.json index 9edb4937e..78d7f6762 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -13,6 +13,12 @@ "path": "github.com/kr/pty", "revision": "fa756f09eeb418bf1cc6268c66ceaad9bb98f598", "revisionTime": "2018-06-20T15:12:22Z" + }, + { + "checksumSHA1": "vUGW1FVEUqtyx20qMHuSZS4i4rU=", + "path": "github.com/mgutz/str", + "revision": "968bf66e3da857419e4f6e71b2d5c9ae95682dc4", + "revisionTime": "2015-06-23T15:38:27Z" } ], "rootPath": "github.com/jesseduffield/lazygit" From 32ecc6d7452d831e18caf4da481a34b6f6c130a8 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 09:09:18 +0100 Subject: [PATCH 042/118] Removed getPushPassUnameView function --- pkg/gui/commit_message_panel.go | 2 +- pkg/gui/gui.go | 2 +- pkg/gui/view_helpers.go | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 3f2225a7b..d2b1de0fb 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -61,7 +61,7 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn waitForGroupActie = true waitForGroup.Add(1) - pushPassUnameView := gui.getPushPassUnameView(g) + pushPassUnameView, _ := g.View("pushPassUname") if passOrUname == "username" { pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") pushPassUnameView.Mask = 0 diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 9725426ba..b67ed7b34 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -269,7 +269,7 @@ func (gui *Gui) layout(g *gocui.Gui) error { } } - if gui.getPushPassUnameView(g) == nil { + if check, _ := g.View("pushPassUname"); check == nil { // doesn't matter where this view starts because it will be hidden if pushPassUnameView, err := g.SetView("pushPassUname", 0, 0, width/2, height/2, 0); err != nil { if err != gocui.ErrUnknownView { diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index a5414448f..8eca29b44 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -290,11 +290,6 @@ func (gui *Gui) getCommitMessageView(g *gocui.Gui) *gocui.View { return v } -func (gui *Gui) getPushPassUnameView(g *gocui.Gui) *gocui.View { - v, _ := g.View("pushPassUname") - return v -} - func (gui *Gui) getBranchesView(g *gocui.Gui) *gocui.View { v, _ := g.View("branches") return v From 8742c4c11077755823b64982d0135e906ea71277 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 09:27:03 +0100 Subject: [PATCH 043/118] Removed some variables and placed them inside the gui struct --- pkg/gui/commit_message_panel.go | 32 +++++++++++++++++--------------- pkg/gui/gui.go | 1 + 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index d2b1de0fb..1b2659593 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -52,14 +52,16 @@ func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { return gui.renderString(g, "options", message) } -var unamePassMessage = "" -var waitForGroup sync.WaitGroup -var waitForGroupActie = false +type credentials struct { + unamePassMessage string + waitForGroup sync.WaitGroup + waitForGroupActie bool +} // waitForPassUname wait for a username or password input from the pushPassUname popup func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUname string) string { - waitForGroupActie = true - waitForGroup.Add(1) + gui.credentials.waitForGroupActie = true + gui.credentials.waitForGroup.Add(1) pushPassUnameView, _ := g.View("pushPassUname") if passOrUname == "username" { @@ -83,8 +85,8 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn }) // wait for username/passwords input - waitForGroup.Wait() - return unamePassMessage + gui.credentials.waitForGroup.Wait() + return gui.credentials.unamePassMessage } func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { @@ -94,10 +96,10 @@ func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { // if not dune the push progress will run forever message = "-" } - unamePassMessage = message - if waitForGroupActie { - waitForGroup.Done() - waitForGroupActie = false + gui.credentials.unamePassMessage = message + if gui.credentials.waitForGroupActie { + gui.credentials.waitForGroup.Done() + gui.credentials.waitForGroupActie = false } err := gui.refreshFiles(g) if err != nil { @@ -124,10 +126,10 @@ func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { if err != nil { return err } - unamePassMessage = "" - if waitForGroupActie { - waitForGroup.Done() - waitForGroupActie = false + gui.credentials.unamePassMessage = "" + if gui.credentials.waitForGroupActie { + gui.credentials.waitForGroup.Done() + gui.credentials.waitForGroupActie = false } return gui.switchFocus(g, v, gui.getFilesView(g)) } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index b67ed7b34..60d22f4e9 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -70,6 +70,7 @@ type Gui struct { Errors SentinelErrors Updater *updates.Updater statusManager *statusManager + credentials credentials } type guiState struct { From 80c6e0a8c422d25f1ef7364999645ac6c718bb9e Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 17:02:39 +0100 Subject: [PATCH 044/118] Fixed pushing forever --- pkg/commands/exec_live_default.go | 9 ++++++--- pkg/commands/os.go | 3 +-- pkg/gui/confirmation_panel.go | 11 ++--------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 685974a1b..b0685b639 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -46,11 +46,10 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s go func() { scanner := bufio.NewScanner(tty) scanner.Split(scanWordsWithNewLines) - loop: for scanner.Scan() { select { case <-stopAsking: - break loop + // just do nothing default: toOutput := strings.Trim(scanner.Text(), " ") cmdOutput = append(cmdOutput, toOutput) @@ -63,8 +62,12 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s waitForBufio.Done() }() - if err = cmd.Wait(); err != nil { + err = cmd.Wait() + go func() { stopAsking <- struct{}{} + }() + <-stopAsking + if err != nil { waitForBufio.Wait() return strings.Join(cmdOutput, " "), err } diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 7f2ddde25..b1f17c85f 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -77,8 +77,7 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err } for prompt, pattern := range prompts { - match, _ := regexp.MatchString(pattern, ttyText) - if match { + if match, _ := regexp.MatchString(pattern, ttyText); match { ttyText = "" return ask(prompt) } diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index ed0b0455a..0961370b8 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -7,7 +7,6 @@ package gui import ( - "log" "strings" "github.com/fatih/color" @@ -85,14 +84,8 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt } func (gui *Gui) onNewPopupPanel() { - _, err := gui.g.SetViewOnBottom("commitMessage") - if err != nil { - log.Fatal(err) - } - _, err = gui.g.SetViewOnBottom("pushPassUname") - if err != nil { - log.Fatal(err) - } + _, _ = gui.g.SetViewOnBottom("commitMessage") + _, _ = gui.g.SetViewOnBottom("pushPassUname") } func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { From bc14b01d03c127f5a92feccd20cb651d88e65e44 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 17:25:35 +0100 Subject: [PATCH 045/118] just a test :) --- test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test.sh b/test.sh index a1ab4301f..e2fd568eb 100755 --- a/test.sh +++ b/test.sh @@ -12,3 +12,4 @@ for d in $( find ./* -maxdepth 10 ! -path "./vendor*" ! -path "./.git*" ! -path fi fi done + \ No newline at end of file From 9fafd7ebc1f100064ae1acd8881d0de296612b81 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 18:10:53 +0100 Subject: [PATCH 046/118] Fixed case that a commit message will break git push --- pkg/commands/os.go | 24 +++++++++++++++++------- test.sh | 1 - 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index b1f17c85f..23f097449 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -70,16 +70,26 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err errMessage, err := c.RunCommandWithOutputLive(command, func(word string) string { ttyText = ttyText + " " + word - // prompt and patterns to check if the user needs to input a username / password - prompts := map[string]string{ - "password": `Password\s*for\s*'.+':`, - "username": `Username\s*for\s*'.+':`, + type Prompt struct { + pattern string + canAskFor bool + } + prompts := map[string]Prompt{ + "password": Prompt{ + pattern: `Password\s*for\s*'.+':`, + canAskFor: true, + }, + "username": Prompt{ + pattern: `Username\s*for\s*'.+':`, + canAskFor: true, + }, } - for prompt, pattern := range prompts { - if match, _ := regexp.MatchString(pattern, ttyText); match { + for askFor, propmt := range prompts { + if match, _ := regexp.MatchString(propmt.pattern, ttyText); match && propmt.canAskFor { + propmt.canAskFor = false ttyText = "" - return ask(prompt) + return ask(askFor) } } diff --git a/test.sh b/test.sh index e2fd568eb..a1ab4301f 100755 --- a/test.sh +++ b/test.sh @@ -12,4 +12,3 @@ for d in $( find ./* -maxdepth 10 ! -path "./vendor*" ! -path "./.git*" ! -path fi fi done - \ No newline at end of file From 18a1070c2c5bdf2a0ea2770b63a23557ac4bfa36 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 10 Nov 2018 18:24:37 +0100 Subject: [PATCH 047/118] Trying to fix circleci --- pkg/commands/os.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 23f097449..eaa1581d4 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -75,11 +75,11 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err canAskFor bool } prompts := map[string]Prompt{ - "password": Prompt{ + "password": { pattern: `Password\s*for\s*'.+':`, canAskFor: true, }, - "username": Prompt{ + "username": { pattern: `Username\s*for\s*'.+':`, canAskFor: true, }, From 0c4c00c1bf62d82064f8a4dc14bdb1169a051865 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 14 Nov 2018 11:14:31 +0100 Subject: [PATCH 048/118] Removed useless channel read --- pkg/commands/exec_live_default.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index b0685b639..ad1c568e5 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -66,7 +66,6 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s go func() { stopAsking <- struct{}{} }() - <-stopAsking if err != nil { waitForBufio.Wait() return strings.Join(cmdOutput, " "), err From 448d9caf1bca747a9d828456ac3e8e7dde0a3df2 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 14 Nov 2018 11:40:32 +0100 Subject: [PATCH 049/118] Fixed typo --- pkg/commands/os.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index eaa1581d4..f64f0732e 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -85,9 +85,9 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err }, } - for askFor, propmt := range prompts { - if match, _ := regexp.MatchString(propmt.pattern, ttyText); match && propmt.canAskFor { - propmt.canAskFor = false + for askFor, prompt := range prompts { + if match, _ := regexp.MatchString(prompt.pattern, ttyText); match && prompt.canAskFor { + prompt.canAskFor = false ttyText = "" return ask(askFor) } From adfc00bcdcaaaab2e1102509d604017c74e97fba Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 23 Nov 2018 13:58:30 +0100 Subject: [PATCH 050/118] Changed the waitForGroup to a channel --- pkg/gui/commit_message_panel.go | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 1b2659593..bb139fb78 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -3,7 +3,6 @@ package gui import ( "strconv" "strings" - "sync" "github.com/jesseduffield/gocui" ) @@ -52,17 +51,11 @@ func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { return gui.renderString(g, "options", message) } -type credentials struct { - unamePassMessage string - waitForGroup sync.WaitGroup - waitForGroupActie bool -} +type credentials chan string // waitForPassUname wait for a username or password input from the pushPassUname popup func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUname string) string { - gui.credentials.waitForGroupActie = true - gui.credentials.waitForGroup.Add(1) - + gui.credentials = make(chan string) pushPassUnameView, _ := g.View("pushPassUname") if passOrUname == "username" { pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") @@ -85,8 +78,8 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn }) // wait for username/passwords input - gui.credentials.waitForGroup.Wait() - return gui.credentials.unamePassMessage + userInput := <-gui.credentials + return userInput } func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { @@ -96,11 +89,7 @@ func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { // if not dune the push progress will run forever message = "-" } - gui.credentials.unamePassMessage = message - if gui.credentials.waitForGroupActie { - gui.credentials.waitForGroup.Done() - gui.credentials.waitForGroupActie = false - } + gui.credentials <- message err := gui.refreshFiles(g) if err != nil { return err @@ -126,11 +115,7 @@ func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { if err != nil { return err } - gui.credentials.unamePassMessage = "" - if gui.credentials.waitForGroupActie { - gui.credentials.waitForGroup.Done() - gui.credentials.waitForGroupActie = false - } + gui.credentials <- "" return gui.switchFocus(g, v, gui.getFilesView(g)) } From 9da1382e09c2e3c43c8e979e26d7614c139d12dd Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sun, 25 Nov 2018 13:15:36 +0100 Subject: [PATCH 051/118] Added credentials popup --- pkg/commands/git.go | 46 ++++++++++++++++++++++++++++++++++++-- pkg/gui/gui.go | 54 ++++++++++++++++++++++++++++++++++++++++++--- pkg/i18n/dutch.go | 6 +++++ pkg/i18n/english.go | 6 +++++ pkg/i18n/polish.go | 6 +++++ 5 files changed, 113 insertions(+), 5 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index d4f4ffd03..9fd675baf 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -63,6 +63,7 @@ type GitCommand struct { Worktree *gogit.Worktree Repo *gogit.Repository Tr *i18n.Localizer + SavedCredentials SavedCredentials getGlobalGitConfig func(string) (string, error) getLocalGitConfig func(string) (string, error) removeFile func(string) error @@ -93,12 +94,19 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer) } } + credentials := SavedCredentials{ + Username: "", + Password: "", + HasAsked: false, + } + return &GitCommand{ Log: log, OSCommand: osCommand, Tr: tr, Worktree: worktree, Repo: repo, + SavedCredentials: credentials, getGlobalGitConfig: gitconfig.Global, getLocalGitConfig: gitconfig.Local, removeFile: os.RemoveAll, @@ -247,9 +255,43 @@ func (c *GitCommand) RenameCommit(name string) error { return c.OSCommand.RunCommand(fmt.Sprintf("git commit --allow-empty --amend -m %s", c.OSCommand.Quote(name))) } +// SavedCredentials are the user's git login credentials +type SavedCredentials struct { + Username string + Password string + HasAsked bool +} + // Fetch fetch git repo -func (c *GitCommand) Fetch() error { - return c.OSCommand.RunCommand("git fetch") +func (c *GitCommand) Fetch(unamePassQuestion func(string) string, runDry bool) error { + newCredentials := SavedCredentials{ + Username: c.SavedCredentials.Username, + Password: c.SavedCredentials.Password, + } + cmd := "git fetch" + if runDry { + cmd += " --dry-run" + } + + err := c.OSCommand.DetectUnamePass(cmd, func(question string) string { + if question == "username" && len(c.SavedCredentials.Username) != 0 { + return c.SavedCredentials.Username + } + if question == "password" && len(c.SavedCredentials.Password) != 0 { + return c.SavedCredentials.Password + } + output := unamePassQuestion(question) + if question == "password" { + newCredentials.Password = output + } else { + newCredentials.Username = output + } + return output + }) + if err == nil { + c.SavedCredentials = newCredentials + } + return err } // ResetToCommit reset to commit diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 6d42aee48..52826c4d7 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -11,10 +11,12 @@ import ( "os" "os/exec" "strings" + "sync" "time" // "strings" + "github.com/fatih/color" "github.com/golang-collections/collections/stack" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands" @@ -350,9 +352,50 @@ func (gui *Gui) promptAnonymousReporting() error { } func (gui *Gui) fetch(g *gocui.Gui) error { - gui.GitCommand.Fetch() + err := gui.GitCommand.Fetch(func(passOrUname string) string { + if !gui.GitCommand.SavedCredentials.HasAsked { + var wg sync.WaitGroup + wg.Add(1) + gui.GitCommand.SavedCredentials.HasAsked = true + close := func(g *gocui.Gui, v *gocui.View) error { + wg.Done() + return nil + } + _ = gui.createConfirmationPanel( + g, + g.CurrentView(), + gui.Tr.SLocalize("RepoRequiresCredentialsTitle"), + gui.Tr.SLocalize("RepoRequiresCredentialsBody"), + close, + close, + ) + wg.Wait() + } + return gui.waitForPassUname(gui.g, gui.g.CurrentView(), passOrUname) + }, false) + + var reTryErr error + if err != nil && strings.Contains(err.Error(), "exit status 128") { + var wg sync.WaitGroup + wg.Add(1) + + currentView := g.CurrentView() + colorFunction := color.New(color.FgRed).SprintFunc() + coloredMessage := colorFunction(strings.TrimSpace(gui.Tr.SLocalize("PassUnameWrong"))) + close := func(g *gocui.Gui, v *gocui.View) error { + wg.Done() + return nil + } + _ = gui.createConfirmationPanel(g, currentView, gui.Tr.SLocalize("Error"), coloredMessage, close, close) + wg.Wait() + reTryErr = gui.fetch(g) + } + gui.refreshStatus(g) - return nil + if reTryErr != nil { + return reTryErr + } + return err } func (gui *Gui) updateLoader(g *gocui.Gui) error { @@ -407,7 +450,12 @@ func (gui *Gui) Run() error { return err } - gui.goEvery(g, time.Second*60, gui.fetch) + go func() { + err := gui.fetch(g) + if err == nil { + gui.goEvery(g, time.Second*10, gui.fetch) + } + }() gui.goEvery(g, time.Second*10, gui.refreshFiles) gui.goEvery(g, time.Millisecond*50, gui.updateLoader) gui.goEvery(g, time.Millisecond*50, gui.renderAppStatus) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index ca88585b8..86b679692 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -412,6 +412,12 @@ func addDutch(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "NoBranchOnRemote", Other: `Deze branch bestaat niet op de remote. U moet het eerst naar de remote pushen.`, + }, &i18n.Message{ + ID: "RepoRequiresCredentialsTitle", + Other: `Repo heeft inloggegevens nodig`, + }, &i18n.Message{ + ID: "RepoRequiresCredentialsBody", + Other: `Lazygit heeft inloggegevens nodig om git fetch te kunnen gebruiken`, }, ) } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index dc4e1537c..07dbeaffe 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -420,6 +420,12 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "NoBranchOnRemote", Other: `This branch doesn't exist on remote. You need to push it to remote first.`, + }, &i18n.Message{ + ID: "RepoRequiresCredentialsTitle", + Other: `Repo requires credentials`, + }, &i18n.Message{ + ID: "RepoRequiresCredentialsBody", + Other: `Lazygit needs credentials to use git fetch`, }, ) } diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index d5a5d6ec0..11c4b574b 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -395,6 +395,12 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "NoBranchOnRemote", Other: `Ta gałąź nie istnieje na zdalnym. Najpierw musisz go odepchnąć na odległość.`, + }, &i18n.Message{ + ID: "RepoRequiresCredentialsTitle", + Other: `Repo requires credentials`, + }, &i18n.Message{ + ID: "RepoRequiresCredentialsBody", + Other: `Lazygit needs credentials to use git fetch`, }, ) } From d91493b5876aeb644dab18b4c41b0ae56fb2f94b Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sun, 25 Nov 2018 13:23:32 +0100 Subject: [PATCH 052/118] Forgot to set the git fetch timeout back to 60s --- pkg/gui/gui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 52826c4d7..69343a95d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -453,7 +453,7 @@ func (gui *Gui) Run() error { go func() { err := gui.fetch(g) if err == nil { - gui.goEvery(g, time.Second*10, gui.fetch) + gui.goEvery(g, time.Second*60, gui.fetch) } }() gui.goEvery(g, time.Second*10, gui.refreshFiles) From 2edd2b74ff68d6f55172c728e9d5f0d2aea69684 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sun, 2 Dec 2018 14:58:18 +0100 Subject: [PATCH 053/118] Removed a lot of useless code --- pkg/commands/git.go | 47 +++++---------------------------------- pkg/gui/branches_panel.go | 5 +++++ pkg/gui/gui.go | 47 +++++++++------------------------------ pkg/gui/keybindings.go | 6 +++++ pkg/i18n/dutch.go | 7 ++---- pkg/i18n/english.go | 7 ++---- pkg/i18n/polish.go | 7 ++---- 7 files changed, 32 insertions(+), 94 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 9fd675baf..2d2710a42 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -63,7 +63,6 @@ type GitCommand struct { Worktree *gogit.Worktree Repo *gogit.Repository Tr *i18n.Localizer - SavedCredentials SavedCredentials getGlobalGitConfig func(string) (string, error) getLocalGitConfig func(string) (string, error) removeFile func(string) error @@ -94,19 +93,12 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer) } } - credentials := SavedCredentials{ - Username: "", - Password: "", - HasAsked: false, - } - return &GitCommand{ Log: log, OSCommand: osCommand, Tr: tr, Worktree: worktree, Repo: repo, - SavedCredentials: credentials, getGlobalGitConfig: gitconfig.Global, getLocalGitConfig: gitconfig.Local, removeFile: os.RemoveAll, @@ -255,43 +247,14 @@ func (c *GitCommand) RenameCommit(name string) error { return c.OSCommand.RunCommand(fmt.Sprintf("git commit --allow-empty --amend -m %s", c.OSCommand.Quote(name))) } -// SavedCredentials are the user's git login credentials -type SavedCredentials struct { - Username string - Password string - HasAsked bool -} - // Fetch fetch git repo -func (c *GitCommand) Fetch(unamePassQuestion func(string) string, runDry bool) error { - newCredentials := SavedCredentials{ - Username: c.SavedCredentials.Username, - Password: c.SavedCredentials.Password, - } - cmd := "git fetch" - if runDry { - cmd += " --dry-run" - } - - err := c.OSCommand.DetectUnamePass(cmd, func(question string) string { - if question == "username" && len(c.SavedCredentials.Username) != 0 { - return c.SavedCredentials.Username +func (c *GitCommand) Fetch(unamePassQuestion func(string) string, canSskForCredentials bool) error { + return c.OSCommand.DetectUnamePass("git fetch", func(question string) string { + if canSskForCredentials { + return unamePassQuestion(question) } - if question == "password" && len(c.SavedCredentials.Password) != 0 { - return c.SavedCredentials.Password - } - output := unamePassQuestion(question) - if question == "password" { - newCredentials.Password = output - } else { - newCredentials.Username = output - } - return output + return "-" }) - if err == nil { - c.SavedCredentials = newCredentials - } - return err } // ResetToCommit reset to commit diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index dbf4b007a..c62538d5c 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -33,6 +33,11 @@ func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error return nil } +func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { + gui.fetch(g, true) + return nil +} + func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error { branch := gui.getSelectedBranch(v) message := gui.Tr.SLocalize("SureForceCheckout") diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 69343a95d..872a00c37 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -11,7 +11,6 @@ import ( "os" "os/exec" "strings" - "sync" "time" // "strings" @@ -351,50 +350,21 @@ func (gui *Gui) promptAnonymousReporting() error { }) } -func (gui *Gui) fetch(g *gocui.Gui) error { +func (gui *Gui) fetch(g *gocui.Gui, canSskForCredentials bool) error { err := gui.GitCommand.Fetch(func(passOrUname string) string { - if !gui.GitCommand.SavedCredentials.HasAsked { - var wg sync.WaitGroup - wg.Add(1) - gui.GitCommand.SavedCredentials.HasAsked = true - close := func(g *gocui.Gui, v *gocui.View) error { - wg.Done() - return nil - } - _ = gui.createConfirmationPanel( - g, - g.CurrentView(), - gui.Tr.SLocalize("RepoRequiresCredentialsTitle"), - gui.Tr.SLocalize("RepoRequiresCredentialsBody"), - close, - close, - ) - wg.Wait() - } return gui.waitForPassUname(gui.g, gui.g.CurrentView(), passOrUname) - }, false) + }, canSskForCredentials) - var reTryErr error - if err != nil && strings.Contains(err.Error(), "exit status 128") { - var wg sync.WaitGroup - wg.Add(1) - - currentView := g.CurrentView() + if canSskForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") { colorFunction := color.New(color.FgRed).SprintFunc() coloredMessage := colorFunction(strings.TrimSpace(gui.Tr.SLocalize("PassUnameWrong"))) close := func(g *gocui.Gui, v *gocui.View) error { - wg.Done() return nil } - _ = gui.createConfirmationPanel(g, currentView, gui.Tr.SLocalize("Error"), coloredMessage, close, close) - wg.Wait() - reTryErr = gui.fetch(g) + _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("Error"), coloredMessage, close, close) } gui.refreshStatus(g) - if reTryErr != nil { - return reTryErr - } return err } @@ -451,9 +421,12 @@ func (gui *Gui) Run() error { } go func() { - err := gui.fetch(g) - if err == nil { - gui.goEvery(g, time.Second*60, gui.fetch) + time.Sleep(time.Second * 60) + err := gui.fetch(g, false) + if err == nil || !strings.Contains(err.Error(), "exit status 128") { + gui.goEvery(g, time.Second*60, func(g *gocui.Gui) error { + return gui.fetch(g, false) + }) } }() gui.goEvery(g, time.Second*10, gui.refreshFiles) diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index e460e4a1c..b926853ba 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -295,6 +295,12 @@ func (gui *Gui) GetKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handleForceCheckout, Description: gui.Tr.SLocalize("forceCheckout"), + }, { + ViewName: "branches", + Key: 'f', + Modifier: gocui.ModNone, + Handler: gui.handleGitFetch, + Description: gui.Tr.SLocalize("fetch"), }, { ViewName: "branches", Key: 'n', diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index 86b679692..5f9e4bf34 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -413,11 +413,8 @@ func addDutch(i18nObject *i18n.Bundle) error { ID: "NoBranchOnRemote", Other: `Deze branch bestaat niet op de remote. U moet het eerst naar de remote pushen.`, }, &i18n.Message{ - ID: "RepoRequiresCredentialsTitle", - Other: `Repo heeft inloggegevens nodig`, - }, &i18n.Message{ - ID: "RepoRequiresCredentialsBody", - Other: `Lazygit heeft inloggegevens nodig om git fetch te kunnen gebruiken`, + ID: "fetch", + Other: `fetch`, }, ) } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 07dbeaffe..929e8834d 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -421,11 +421,8 @@ func addEnglish(i18nObject *i18n.Bundle) error { ID: "NoBranchOnRemote", Other: `This branch doesn't exist on remote. You need to push it to remote first.`, }, &i18n.Message{ - ID: "RepoRequiresCredentialsTitle", - Other: `Repo requires credentials`, - }, &i18n.Message{ - ID: "RepoRequiresCredentialsBody", - Other: `Lazygit needs credentials to use git fetch`, + ID: "fetch", + Other: `fetch`, }, ) } diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index 11c4b574b..ec59288fa 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -396,11 +396,8 @@ func addPolish(i18nObject *i18n.Bundle) error { ID: "NoBranchOnRemote", Other: `Ta gałąź nie istnieje na zdalnym. Najpierw musisz go odepchnąć na odległość.`, }, &i18n.Message{ - ID: "RepoRequiresCredentialsTitle", - Other: `Repo requires credentials`, - }, &i18n.Message{ - ID: "RepoRequiresCredentialsBody", - Other: `Lazygit needs credentials to use git fetch`, + ID: "fetch", + Other: `fetch`, }, ) } From c269ad137090479be68b5353a05f2ed036b3dfc0 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sun, 2 Dec 2018 15:06:51 +0100 Subject: [PATCH 054/118] Made the bot happy --- pkg/gui/branches_panel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index c62538d5c..13267f29d 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -34,7 +34,7 @@ func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error } func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { - gui.fetch(g, true) + _ = gui.fetch(g, true) return nil } From f6e83cdbdff57e8f3403c6b7f7acd22b88ca8613 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Thu, 6 Dec 2018 08:26:05 +0100 Subject: [PATCH 055/118] Started working on the popup --- pkg/gui/branches_panel.go | 11 +++++------ pkg/gui/gui.go | 5 +++-- pkg/i18n/dutch.go | 6 ++++++ pkg/i18n/english.go | 6 ++++++ pkg/i18n/polish.go | 6 ++++++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index 13267f29d..43c585730 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -97,14 +97,14 @@ func (gui *Gui) deleteBranch(g *gocui.Gui, v *gocui.View, force bool) error { func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *commands.Branch, force bool) error { title := gui.Tr.SLocalize("DeleteBranch") - var messageId string + var messageID string if force { - messageId = "ForceDeleteBranchMessage" + messageID = "ForceDeleteBranchMessage" } else { - messageId = "DeleteBranchMessage" + messageID = "DeleteBranchMessage" } message := gui.Tr.TemplateLocalize( - messageId, + messageID, Teml{ "selectedBranchName": selectedBranch.Name, }, @@ -114,9 +114,8 @@ func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *c errMessage := err.Error() if !force && strings.Contains(errMessage, "is not fully merged") { return gui.deleteNamedBranch(g, v, selectedBranch, true) - } else { - return gui.createErrorPanel(g, errMessage) } + return gui.createErrorPanel(g, errMessage) } return gui.refreshSidePanels(g) }, nil) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 872a00c37..b443d008d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -421,9 +421,10 @@ func (gui *Gui) Run() error { } go func() { - time.Sleep(time.Second * 60) err := gui.fetch(g, false) - if err == nil || !strings.Contains(err.Error(), "exit status 128") { + if err != nil && strings.Contains(err.Error(), "exit status 128") { + gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) + } else { gui.goEvery(g, time.Second*60, func(g *gocui.Gui) error { return gui.fetch(g, false) }) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index 5f9e4bf34..2f5a3a404 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -415,6 +415,12 @@ func addDutch(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "fetch", Other: `fetch`, + }, &i18n.Message{ + ID: "NoAutomaticGitFetchTitle", + Other: `No automatic git fetch`, + }, &i18n.Message{ + ID: "NoAutomaticGitFetchBody", + Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, }, ) } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 929e8834d..2ef1d8597 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -423,6 +423,12 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "fetch", Other: `fetch`, + }, &i18n.Message{ + ID: "NoAutomaticGitFetchTitle", + Other: `No automatic git fetch`, + }, &i18n.Message{ + ID: "NoAutomaticGitFetchBody", + Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, }, ) } diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index ec59288fa..d2b4a84ed 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -398,6 +398,12 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "fetch", Other: `fetch`, + }, &i18n.Message{ + ID: "NoAutomaticGitFetchTitle", + Other: `No automatic git fetch`, + }, &i18n.Message{ + ID: "NoAutomaticGitFetchBody", + Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, }, ) } From 67115436347dec591d12a1a31d1c971915cf0b05 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Thu, 6 Dec 2018 08:28:15 +0100 Subject: [PATCH 056/118] Made the gobot happy again --- pkg/gui/gui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index b443d008d..45284130c 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -423,7 +423,7 @@ func (gui *Gui) Run() error { go func() { err := gui.fetch(g, false) if err != nil && strings.Contains(err.Error(), "exit status 128") { - gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) + _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) } else { gui.goEvery(g, time.Second*60, func(g *gocui.Gui) error { return gui.fetch(g, false) From 89c272eed586b77274e82776137e65f1a5df54d4 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Thu, 6 Dec 2018 08:33:04 +0100 Subject: [PATCH 057/118] Removed the tabs for spaces --- pkg/i18n/dutch.go | 2 +- pkg/i18n/english.go | 2 +- pkg/i18n/polish.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index bd7305735..bc41c0912 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -421,7 +421,7 @@ func addDutch(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, - }, &i18n.Message{ + }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, }, &i18n.Message{ diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 5e0c8109f..077f0b13a 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -429,7 +429,7 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, - }, &i18n.Message{ + }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, }, &i18n.Message{ diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index e7a6e64cc..1c129eb76 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -404,7 +404,7 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, - }, &i18n.Message{ + }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, }, &i18n.Message{ From 21a808a52bc3cd56500e3203d631fc8ccd12e096 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Thu, 6 Dec 2018 08:38:40 +0100 Subject: [PATCH 058/118] Renamed branch to branches --- pkg/i18n/dutch.go | 2 +- pkg/i18n/english.go | 2 +- pkg/i18n/polish.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index bc41c0912..afc20d32b 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -420,7 +420,7 @@ func addDutch(i18nObject *i18n.Bundle) error { Other: `No automatic git fetch`, }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", - Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, + Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run git fetch manually`, }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 077f0b13a..9ca6af074 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -428,7 +428,7 @@ func addEnglish(i18nObject *i18n.Bundle) error { Other: `No automatic git fetch`, }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", - Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, + Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run git fetch manually`, }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index 1c129eb76..271282c5b 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -403,7 +403,7 @@ func addPolish(i18nObject *i18n.Bundle) error { Other: `No automatic git fetch`, }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", - Other: `Lazygit can't use "git fetch" in a private repo use f in the branch panel to run git fetch manually`, + Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run git fetch manually`, }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, From 6d0fa8bc2926af9c17c7546c447613ae21ef19fc Mon Sep 17 00:00:00 2001 From: mjarkk Date: Thu, 6 Dec 2018 09:05:51 +0100 Subject: [PATCH 059/118] Made some small inprovements --- pkg/gui/branches_panel.go | 3 +-- pkg/gui/gui.go | 10 +++++----- pkg/i18n/dutch.go | 4 ++-- pkg/i18n/english.go | 2 +- pkg/i18n/polish.go | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index 43c585730..7d1ca5782 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -34,8 +34,7 @@ func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error } func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { - _ = gui.fetch(g, true) - return nil + return gui.fetch(g, v, true) } func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error { diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 767a25bde..aab73e738 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -371,9 +371,9 @@ func (gui *Gui) promptAnonymousReporting() error { }) } -func (gui *Gui) fetch(g *gocui.Gui, canSskForCredentials bool) error { +func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canSskForCredentials bool) error { err := gui.GitCommand.Fetch(func(passOrUname string) string { - return gui.waitForPassUname(gui.g, gui.g.CurrentView(), passOrUname) + return gui.waitForPassUname(gui.g, v, passOrUname) }, canSskForCredentials) if canSskForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") { @@ -382,7 +382,7 @@ func (gui *Gui) fetch(g *gocui.Gui, canSskForCredentials bool) error { close := func(g *gocui.Gui, v *gocui.View) error { return nil } - _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("Error"), coloredMessage, close, close) + _ = gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("Error"), coloredMessage, close, close) } gui.refreshStatus(g) @@ -442,12 +442,12 @@ func (gui *Gui) Run() error { } go func() { - err := gui.fetch(g, false) + err := gui.fetch(g, g.CurrentView(), false) if err != nil && strings.Contains(err.Error(), "exit status 128") { _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) } else { gui.goEvery(g, time.Second*60, func(g *gocui.Gui) error { - return gui.fetch(g, false) + return gui.fetch(g, g.CurrentView(), false) }) } }() diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index afc20d32b..a29c8c8c0 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -417,10 +417,10 @@ func addDutch(i18nObject *i18n.Bundle) error { Other: `fetch`, }, &i18n.Message{ ID: "NoAutomaticGitFetchTitle", - Other: `No automatic git fetch`, + Other: `Geen automatiese git fetch`, }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", - Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run git fetch manually`, + Other: `Lazygit kan niet "git fetch" uitvoeren in een privé repository, gebruik f in het branches paneel om "git fetch" manueel uit te voeren`, }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 9ca6af074..872e9783e 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -428,7 +428,7 @@ func addEnglish(i18nObject *i18n.Bundle) error { Other: `No automatic git fetch`, }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", - Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run git fetch manually`, + Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run "git fetch" manually`, }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index 271282c5b..3ffa980ae 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -403,7 +403,7 @@ func addPolish(i18nObject *i18n.Bundle) error { Other: `No automatic git fetch`, }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", - Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run git fetch manually`, + Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run "git fetch" manually`, }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, From ced81e11f048a954b707996d78487e3a6cabd569 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Thu, 6 Dec 2018 22:05:16 +0100 Subject: [PATCH 060/118] Only show private repo popup when opening repo for first time --- pkg/config/app_config.go | 8 +++++--- pkg/gui/gui.go | 2 +- pkg/gui/recent_repos_panel.go | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index f789349b4..c01bd7df8 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -236,14 +236,16 @@ confirmOnQuit: false // AppState stores data between runs of the app like when the last update check // was performed and which other repos have been checked out type AppState struct { - LastUpdateCheck int64 - RecentRepos []string + LastUpdateCheck int64 + RecentRepos []string + RecentPrivateRepos []string } func getDefaultAppState() []byte { return []byte(` lastUpdateCheck: 0 - recentRepos: [] + recentRepos: [] + RecentPrivateRepos: [] `) } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index aab73e738..e3d80df12 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -443,7 +443,7 @@ func (gui *Gui) Run() error { go func() { err := gui.fetch(g, g.CurrentView(), false) - if err != nil && strings.Contains(err.Error(), "exit status 128") { + if err != nil && strings.Contains(err.Error(), "exit status 128") && gui.canShowIsPrivateRepo() { _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) } else { gui.goEvery(g, time.Second*60, func(g *gocui.Gui) error { diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index 98da6a9c2..2b7d05f0a 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -59,6 +59,24 @@ func (gui *Gui) updateRecentRepoList() error { return gui.Config.SaveAppState() } +// canShowIsPrivateRepo returns true if a private repo is never opend before in lazygit +func (gui *Gui) canShowIsPrivateRepo() bool { + repos := gui.Config.GetAppState().RecentPrivateRepos + currentRepo, err := os.Getwd() + for _, repo := range repos { + if currentRepo == repo { + return false + } + } + if err != nil { + return true + } + gui.Config.GetAppState().RecentPrivateRepos = newRecentReposList(repos, currentRepo) + _ = gui.Config.SaveAppState() + return true +} + +// newRecentReposList returns a new repo list with a new entry but only when it doesn't exist yet func newRecentReposList(recentRepos []string, currentRepo string) []string { newRepos := []string{currentRepo} for _, repo := range recentRepos { From 8f904ffd720714fb315c69d1c44d6974842ab051 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 7 Dec 2018 14:56:29 +0100 Subject: [PATCH 061/118] Working popup --- pkg/gui/branches_panel.go | 24 +++++++++++++++++++++++- pkg/gui/commit_message_panel.go | 2 +- pkg/gui/gui.go | 13 ++++++++----- pkg/i18n/dutch.go | 7 +++++-- pkg/i18n/english.go | 3 +++ pkg/i18n/polish.go | 3 +++ 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index 7d1ca5782..059f0af50 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -34,7 +34,29 @@ func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error } func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { - return gui.fetch(g, v, true) + if err := gui.createMessagePanel(g, v, "", gui.Tr.SLocalize("FetchWait")); err != nil { + return err + } + go func() { + unamePassOpend, err := gui.fetch(g, v, true) + if err != nil { + errMessage := err.Error() + if errMessage == "exit status 128" { + errMessage = gui.Tr.SLocalize("PassUnameWrong") + } + _ = gui.createErrorPanel(g, errMessage) + } + if unamePassOpend { + _, _ = g.SetViewOnBottom("pushPassUname") + _ = g.DeleteView("pushPassUname") + } + if err == nil { + _ = gui.closeConfirmationPrompt(g) + _ = gui.refreshCommits(g) + _ = gui.refreshStatus(g) + } + }() + return nil } func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error { diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 560f64415..14e3ab795 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -115,7 +115,7 @@ func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { if err != nil { return err } - gui.credentials <- "" + gui.credentials <- "-" return gui.switchFocus(g, v, gui.getFilesView(g)) } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index e3d80df12..0004abf1b 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -371,8 +371,10 @@ func (gui *Gui) promptAnonymousReporting() error { }) } -func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canSskForCredentials bool) error { - err := gui.GitCommand.Fetch(func(passOrUname string) string { +func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canSskForCredentials bool) (unamePassOpend bool, err error) { + unamePassOpend = false + err = gui.GitCommand.Fetch(func(passOrUname string) string { + unamePassOpend = true return gui.waitForPassUname(gui.g, v, passOrUname) }, canSskForCredentials) @@ -386,7 +388,7 @@ func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canSskForCredentials bool) er } gui.refreshStatus(g) - return err + return unamePassOpend, err } func (gui *Gui) updateLoader(g *gocui.Gui) error { @@ -442,12 +444,13 @@ func (gui *Gui) Run() error { } go func() { - err := gui.fetch(g, g.CurrentView(), false) + _, err := gui.fetch(g, g.CurrentView(), false) if err != nil && strings.Contains(err.Error(), "exit status 128") && gui.canShowIsPrivateRepo() { _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) } else { gui.goEvery(g, time.Second*60, func(g *gocui.Gui) error { - return gui.fetch(g, g.CurrentView(), false) + _, err := gui.fetch(g, g.CurrentView(), false) + return err }) } }() diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index a29c8c8c0..f62b60b90 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -138,10 +138,13 @@ func addDutch(i18nObject *i18n.Bundle) error { Other: "Dit is geen bestand", }, &i18n.Message{ ID: "PullWait", - Other: "Pulling...", + Other: "Pullen...", }, &i18n.Message{ ID: "PushWait", - Other: "Pushing...", + Other: "Pushen...", + }, &i18n.Message{ + ID: "FetchWait", + Other: "Fetchen...", }, &i18n.Message{ ID: "FileNoMergeCons", Other: "Dit bestand heeft geen merge conflicten", diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 872e9783e..451b7bc0e 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -150,6 +150,9 @@ func addEnglish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "PushWait", Other: "Pushing...", + }, &i18n.Message{ + ID: "FetchWait", + Other: "Fetching...", }, &i18n.Message{ ID: "FileNoMergeCons", Other: "This file has no merge conflicts", diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index 3ffa980ae..8470045e0 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -131,6 +131,9 @@ func addPolish(i18nObject *i18n.Bundle) error { }, &i18n.Message{ ID: "PushWait", Other: "Wypychanie zmian...", + }, &i18n.Message{ + ID: "FetchWait", + Other: "Fetching...", }, &i18n.Message{ ID: "FileNoMergeCons", Other: "Ten plik nie powoduje konfliktów scalania", From 2f7bd2896c27df9cee77384ed8b2d2213cda23c8 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 7 Dec 2018 15:46:49 +0100 Subject: [PATCH 062/118] Fixed error when there is no state.yml --- pkg/config/app_config.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index c01bd7df8..f0d9e7015 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -242,11 +242,11 @@ type AppState struct { } func getDefaultAppState() []byte { - return []byte(` - lastUpdateCheck: 0 - recentRepos: [] - RecentPrivateRepos: [] -`) + return []byte(` + lastUpdateCheck: 0 + recentRepos: [] + RecentPrivateRepos: [] + `) } // // commenting this out until we use it again From cf5a85b80fcd4bdcd186f0cd00b89b30b2bcad72 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 7 Dec 2018 19:22:22 +0100 Subject: [PATCH 063/118] Intro message stays on screen now --- pkg/gui/gui.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 0004abf1b..44c63abe1 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -1,6 +1,7 @@ package gui import ( + "sync" // "io" // "io/ioutil" @@ -72,6 +73,7 @@ type Gui struct { Updater *updates.Updater statusManager *statusManager credentials credentials + introAgree sync.WaitGroup } type stagingState struct { @@ -365,8 +367,10 @@ func (gui *Gui) layout(g *gocui.Gui) error { 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 { + gui.introAgree.Done() return gui.Config.WriteToUserConfig("reporting", "on") }, func(g *gocui.Gui, v *gocui.View) error { + gui.introAgree.Done() return gui.Config.WriteToUserConfig("reporting", "off") }) } @@ -443,9 +447,14 @@ func (gui *Gui) Run() error { return err } + if gui.Config.GetUserConfig().GetString("reporting") == "undetermined" { + gui.introAgree.Add(1) + } + go func() { _, err := gui.fetch(g, g.CurrentView(), false) if err != nil && strings.Contains(err.Error(), "exit status 128") && gui.canShowIsPrivateRepo() { + gui.introAgree.Wait() _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) } else { gui.goEvery(g, time.Second*60, func(g *gocui.Gui) error { From 270658fc009dc254a80501fc1c9f5f82acd27239 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 8 Dec 2018 16:40:22 +0100 Subject: [PATCH 064/118] Made code ready to merge to master's latest commit --- pkg/gui/files_panel.go | 3 +-- pkg/gui/gui.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 6b52970c3..cd8df1859 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -416,8 +416,7 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error } if err == nil { _ = gui.closeConfirmationPrompt(g) - _ = gui.refreshCommits(g) - _ = gui.refreshStatus(g) + _ = gui.refreshSidePanels(g) } }() return nil diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 44c63abe1..70e61c8d5 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -463,7 +463,7 @@ func (gui *Gui) Run() error { }) } }() - gui.goEvery(g, time.Second*10, gui.refreshFiles) + gui.goEvery(g, time.Second*2, gui.refreshFiles) gui.goEvery(g, time.Millisecond*50, gui.updateLoader) gui.goEvery(g, time.Millisecond*50, gui.renderAppStatus) From efb82a58aea920b21c6c715dc0ee37012ad4cebb Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sat, 8 Dec 2018 16:54:00 +0100 Subject: [PATCH 065/118] Tried to fix circleci error --- pkg/config/app_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index f0d9e7015..8ac886257 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -242,7 +242,7 @@ type AppState struct { } func getDefaultAppState() []byte { - return []byte(` + return []byte(` lastUpdateCheck: 0 recentRepos: [] RecentPrivateRepos: [] From f14effe5f5d72ef5344be9d6e3057cca7c6d2c81 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Sun, 9 Dec 2018 13:04:19 +0100 Subject: [PATCH 066/118] Worked and fixed a view comments --- pkg/commands/exec_live_default.go | 2 +- pkg/commands/git.go | 4 ++-- pkg/commands/os.go | 5 ++++- pkg/config/app_config.go | 2 +- pkg/gui/branches_panel.go | 13 +++++++------ pkg/gui/files_panel.go | 26 ++++++++++++++------------ 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index ad1c568e5..5fb84c81e 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -31,7 +31,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s tty, err := pty.Start(cmd) if err != nil { - return errorMessage, err + return "", err } stopAsking := make(chan struct{}) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index d9230c4eb..e708d5c55 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -261,9 +261,9 @@ func (c *GitCommand) RenameCommit(name string) error { } // Fetch fetch git repo -func (c *GitCommand) Fetch(unamePassQuestion func(string) string, canSskForCredentials bool) error { +func (c *GitCommand) Fetch(unamePassQuestion func(string) string, canAskForCredentials bool) error { return c.OSCommand.DetectUnamePass("git fetch", func(question string) string { - if canSskForCredentials { + if canAskForCredentials { return unamePassQuestion(question) } return "-" diff --git a/pkg/commands/os.go b/pkg/commands/os.go index faf6c5aec..037f6da2e 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -87,7 +87,10 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err } for askFor, prompt := range prompts { - if match, _ := regexp.MatchString(prompt.pattern, ttyText); match && prompt.canAskFor { + if !prompt.canAskFor { + continue + } + if match, _ := regexp.MatchString(prompt.pattern, ttyText); match { prompt.canAskFor = false ttyText = "" return ask(askFor) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 8ac886257..48cf2df35 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -245,7 +245,7 @@ func getDefaultAppState() []byte { return []byte(` lastUpdateCheck: 0 recentRepos: [] - RecentPrivateRepos: [] + recentPrivateRepos: [] `) } diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index e54d6e8c1..26b42aba2 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -136,12 +136,13 @@ func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { errMessage = gui.Tr.SLocalize("PassUnameWrong") } _ = gui.createErrorPanel(g, errMessage) - } - if unamePassOpend { - _, _ = g.SetViewOnBottom("pushPassUname") - _ = g.DeleteView("pushPassUname") - } - if err == nil { + if unamePassOpend { + _ = g.DeleteView("pushPassUname") + } + } else { + if unamePassOpend { + _ = g.DeleteView("pushPassUname") + } _ = gui.closeConfirmationPrompt(g) _ = gui.refreshCommits(g) _ = gui.refreshStatus(g) diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 7942e421b..127a0d96a 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -394,12 +394,13 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { errMessage = gui.Tr.SLocalize("PassUnameWrong") } _ = gui.createErrorPanel(g, errMessage) - } - if unamePassOpend { - _, _ = g.SetViewOnBottom("pushPassUname") - _ = g.DeleteView("pushPassUname") - } - if err == nil { + if unamePassOpend { + _ = g.DeleteView("pushPassUname") + } + } else { + if unamePassOpend { + _ = g.DeleteView("pushPassUname") + } _ = gui.closeConfirmationPrompt(g) _ = gui.refreshCommits(g) _ = gui.refreshStatus(g) @@ -426,12 +427,13 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error errMessage = gui.Tr.SLocalize("PassUnameWrong") } _ = gui.createErrorPanel(g, errMessage) - } - if unamePassOpend { - _, _ = g.SetViewOnBottom("pushPassUname") - _ = g.DeleteView("pushPassUname") - } - if err == nil { + if unamePassOpend { + _ = g.DeleteView("pushPassUname") + } + } else { + if unamePassOpend { + _ = g.DeleteView("pushPassUname") + } _ = gui.closeConfirmationPrompt(g) _ = gui.refreshSidePanels(g) } From 696d6dc20caa6a39529031287f33e2cb15093573 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 10 Dec 2018 07:43:32 +0100 Subject: [PATCH 067/118] Fixed loop before error check --- pkg/gui/recent_repos_panel.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index 2b7d05f0a..6f528896e 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -63,14 +63,14 @@ func (gui *Gui) updateRecentRepoList() error { func (gui *Gui) canShowIsPrivateRepo() bool { repos := gui.Config.GetAppState().RecentPrivateRepos currentRepo, err := os.Getwd() + if err != nil { + return true + } for _, repo := range repos { if currentRepo == repo { return false } } - if err != nil { - return true - } gui.Config.GetAppState().RecentPrivateRepos = newRecentReposList(repos, currentRepo) _ = gui.Config.SaveAppState() return true From 3c17bf761a63310e3760980b07b003b4ba44e0e3 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 10 Dec 2018 07:46:26 +0100 Subject: [PATCH 068/118] Better name for this function --- pkg/gui/gui.go | 2 +- pkg/gui/recent_repos_panel.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index d47913c32..f9cf16475 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -514,7 +514,7 @@ func (gui *Gui) Run() error { go func() { _, err := gui.fetch(g, g.CurrentView(), false) - if err != nil && strings.Contains(err.Error(), "exit status 128") && gui.canShowIsPrivateRepo() { + if err != nil && strings.Contains(err.Error(), "exit status 128") && gui.IsNewPrivateRepo() { gui.introAgree.Wait() _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) } else { diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index 6f528896e..4ee990076 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -59,8 +59,8 @@ func (gui *Gui) updateRecentRepoList() error { return gui.Config.SaveAppState() } -// canShowIsPrivateRepo returns true if a private repo is never opend before in lazygit -func (gui *Gui) canShowIsPrivateRepo() bool { +// IsNewPrivateRepo returns true if a private repo is never opend before in lazygit +func (gui *Gui) IsNewPrivateRepo() bool { repos := gui.Config.GetAppState().RecentPrivateRepos currentRepo, err := os.Getwd() if err != nil { From 61c2778de1eaf288f40bc2b475644724a7b6988a Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 10 Dec 2018 07:51:06 +0100 Subject: [PATCH 069/118] Changed pushPassUname name to credentials --- pkg/gui/branches_panel.go | 4 ++-- pkg/gui/commit_message_panel.go | 22 +++++++++++----------- pkg/gui/confirmation_panel.go | 2 +- pkg/gui/files_panel.go | 8 ++++---- pkg/gui/gui.go | 14 +++++++------- pkg/gui/keybindings.go | 4 ++-- pkg/gui/view_helpers.go | 4 ++-- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index 26b42aba2..ebb31e17f 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -137,11 +137,11 @@ func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { } _ = gui.createErrorPanel(g, errMessage) if unamePassOpend { - _ = g.DeleteView("pushPassUname") + _ = g.DeleteView("credentials") } } else { if unamePassOpend { - _ = g.DeleteView("pushPassUname") + _ = g.DeleteView("credentials") } _ = gui.closeConfirmationPrompt(g) _ = gui.refreshCommits(g) diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 85497ef87..7468ec941 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -53,23 +53,23 @@ func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { type credentials chan string -// waitForPassUname wait for a username or password input from the pushPassUname popup +// waitForPassUname wait for a username or password input from the credentials popup func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUname string) string { gui.credentials = make(chan string) - pushPassUnameView, _ := g.View("pushPassUname") + credentialsView, _ := g.View("credentials") if passOrUname == "username" { - pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") - pushPassUnameView.Mask = 0 + credentialsView.Title = gui.Tr.SLocalize("PushUsername") + credentialsView.Mask = 0 } else { - pushPassUnameView.Title = gui.Tr.SLocalize("PushPassword") - pushPassUnameView.Mask = '*' + credentialsView.Title = gui.Tr.SLocalize("PushPassword") + credentialsView.Mask = '*' } g.Update(func(g *gocui.Gui) error { - _, err := g.SetViewOnTop("pushPassUname") + _, err := g.SetViewOnTop("credentials") if err != nil { return err } - err = gui.switchFocus(g, currentView, pushPassUnameView) + err = gui.switchFocus(g, currentView, credentialsView) if err != nil { return err } @@ -99,7 +99,7 @@ func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { if err != nil { return err } - _, err = g.SetViewOnBottom("pushPassUname") + _, err = g.SetViewOnBottom("credentials") if err != nil { return err } @@ -111,7 +111,7 @@ func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { - _, err := g.SetViewOnBottom("pushPassUname") + _, err := g.SetViewOnBottom("credentials") if err != nil { return err } @@ -120,7 +120,7 @@ func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) handlePushFocused(g *gocui.Gui, v *gocui.View) error { - if _, err := g.SetViewOnTop("pushPassUname"); err != nil { + if _, err := g.SetViewOnTop("credentials"); err != nil { return err } diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index 521bd827d..99455a30c 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -86,7 +86,7 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt func (gui *Gui) onNewPopupPanel() { _, _ = gui.g.SetViewOnBottom("commitMessage") - _, _ = gui.g.SetViewOnBottom("pushPassUname") + _, _ = gui.g.SetViewOnBottom("credentials") } func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 127a0d96a..6e525e006 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -395,11 +395,11 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { } _ = gui.createErrorPanel(g, errMessage) if unamePassOpend { - _ = g.DeleteView("pushPassUname") + _ = g.DeleteView("credentials") } } else { if unamePassOpend { - _ = g.DeleteView("pushPassUname") + _ = g.DeleteView("credentials") } _ = gui.closeConfirmationPrompt(g) _ = gui.refreshCommits(g) @@ -428,11 +428,11 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error } _ = gui.createErrorPanel(g, errMessage) if unamePassOpend { - _ = g.DeleteView("pushPassUname") + _ = g.DeleteView("credentials") } } else { if unamePassOpend { - _ = g.DeleteView("pushPassUname") + _ = g.DeleteView("credentials") } _ = gui.closeConfirmationPrompt(g) _ = gui.refreshSidePanels(g) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index f9cf16475..33a8cdc43 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -341,20 +341,20 @@ func (gui *Gui) layout(g *gocui.Gui) error { } } - if check, _ := g.View("pushPassUname"); check == nil { + if check, _ := g.View("credentials"); check == nil { // doesn't matter where this view starts because it will be hidden - if pushPassUnameView, err := g.SetView("pushPassUname", 0, 0, width/2, height/2, 0); err != nil { + if credentialsView, err := g.SetView("credentials", 0, 0, width/2, height/2, 0); err != nil { if err != gocui.ErrUnknownView { return err } - _, err := g.SetViewOnBottom("pushPassUname") + _, err := g.SetViewOnBottom("credentials") if err != nil { return err } - pushPassUnameView.Title = gui.Tr.SLocalize("PushUsername") - pushPassUnameView.FgColor = gocui.ColorWhite - pushPassUnameView.Editable = true - pushPassUnameView.Editor = gocui.EditorFunc(gui.simpleEditor) + credentialsView.Title = gui.Tr.SLocalize("PushUsername") + credentialsView.FgColor = gocui.ColorWhite + credentialsView.Editable = true + credentialsView.Editor = gocui.EditorFunc(gui.simpleEditor) } } diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 978f6dcdf..1823c2955 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -394,12 +394,12 @@ func (gui *Gui) GetKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handleCommitClose, }, { - ViewName: "pushPassUname", + ViewName: "credentials", Key: gocui.KeyEnter, Modifier: gocui.ModNone, Handler: gui.handlePushConfirm, }, { - ViewName: "pushPassUname", + ViewName: "credentials", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: gui.handlePushClose, diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 8ed764c6f..1046fb32e 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -101,7 +101,7 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error { return nil case "commitMessage": return gui.handleCommitFocused(g, v) - case "pushPassUname": + case "credentials": return gui.handlePushFocused(g, v) case "main": // TODO: pull this out into a 'view focused' function @@ -303,7 +303,7 @@ func (gui *Gui) currentViewName(g *gocui.Gui) string { func (gui *Gui) resizeCurrentPopupPanel(g *gocui.Gui) error { v := g.CurrentView() - if v.Name() == "commitMessage" || v.Name() == "pushPassUname" || v.Name() == "confirmation" { + if v.Name() == "commitMessage" || v.Name() == "credentials" || v.Name() == "confirmation" { return gui.resizePopupPanel(g, v) } return nil From 8e3f5e19e0ad1b9ebfe6c19d47cc6538f3fa9085 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 10 Dec 2018 08:04:22 +0100 Subject: [PATCH 070/118] Changed some other names --- pkg/gui/commit_message_panel.go | 4 ++-- pkg/gui/gui.go | 2 +- pkg/i18n/dutch.go | 6 +++--- pkg/i18n/english.go | 4 ++-- pkg/i18n/polish.go | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 7468ec941..4ba9a3d97 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -58,10 +58,10 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn gui.credentials = make(chan string) credentialsView, _ := g.View("credentials") if passOrUname == "username" { - credentialsView.Title = gui.Tr.SLocalize("PushUsername") + credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername") credentialsView.Mask = 0 } else { - credentialsView.Title = gui.Tr.SLocalize("PushPassword") + credentialsView.Title = gui.Tr.SLocalize("CredentialsPassword") credentialsView.Mask = '*' } g.Update(func(g *gocui.Gui) error { diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 33a8cdc43..a8a393620 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -351,7 +351,7 @@ func (gui *Gui) layout(g *gocui.Gui) error { if err != nil { return err } - credentialsView.Title = gui.Tr.SLocalize("PushUsername") + credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername") credentialsView.FgColor = gocui.ColorWhite credentialsView.Editable = true credentialsView.Editor = gocui.EditorFunc(gui.simpleEditor) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index bd5a30e6e..352f29698 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -32,10 +32,10 @@ func addDutch(i18nObject *i18n.Bundle) error { ID: "CommitMessage", Other: "Commit bericht", }, &i18n.Message{ - ID: "PushUsername", - Other: "Gebruikers naam", + ID: "CredentialsUsername", + Other: "Gebruikersnaam", }, &i18n.Message{ - ID: "PushPassword", + ID: "CredentialsPassword", Other: "Wachtwoord", }, &i18n.Message{ ID: "PassUnameWrong", diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 5806c8c03..26186c6d9 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -40,10 +40,10 @@ func addEnglish(i18nObject *i18n.Bundle) error { ID: "CommitMessage", Other: "Commit message", }, &i18n.Message{ - ID: "PushUsername", + ID: "CredentialsUsername", Other: "Username", }, &i18n.Message{ - ID: "PushPassword", + ID: "CredentialsPassword", Other: "Password", }, &i18n.Message{ ID: "PassUnameWrong", diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index c8ff0c186..d08ffc170 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -30,10 +30,10 @@ func addPolish(i18nObject *i18n.Bundle) error { ID: "CommitMessage", Other: "Wiadomość commita", }, &i18n.Message{ - ID: "PushUsername", + ID: "CredentialsUsername", Other: "Username", }, &i18n.Message{ - ID: "PushPassword", + ID: "CredentialsPassword", Other: "Password", }, &i18n.Message{ ID: "PassUnameWrong", From 50f20de8f34b53b808710e91fea501cb1e26ff89 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 10 Dec 2018 08:22:52 +0100 Subject: [PATCH 071/118] Removed a lot of duplicated code --- pkg/gui/branches_panel.go | 18 +----------------- pkg/gui/files_panel.go | 36 ++---------------------------------- pkg/gui/view_helpers.go | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 51 deletions(-) diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index ebb31e17f..b6cc45664 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -130,23 +130,7 @@ func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error { } go func() { unamePassOpend, err := gui.fetch(g, v, true) - if err != nil { - errMessage := err.Error() - if errMessage == "exit status 128" { - errMessage = gui.Tr.SLocalize("PassUnameWrong") - } - _ = gui.createErrorPanel(g, errMessage) - if unamePassOpend { - _ = g.DeleteView("credentials") - } - } else { - if unamePassOpend { - _ = g.DeleteView("credentials") - } - _ = gui.closeConfirmationPrompt(g) - _ = gui.refreshCommits(g) - _ = gui.refreshStatus(g) - } + gui.HandleCredentialsPopup(g, unamePassOpend, err) }() return nil } diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 6e525e006..a89138161 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -388,24 +388,7 @@ func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { unamePassOpend = true return gui.waitForPassUname(g, v, passOrUname) }) - if err != nil { - errMessage := err.Error() - if errMessage == "exit status 128" { - errMessage = gui.Tr.SLocalize("PassUnameWrong") - } - _ = gui.createErrorPanel(g, errMessage) - if unamePassOpend { - _ = g.DeleteView("credentials") - } - } else { - if unamePassOpend { - _ = g.DeleteView("credentials") - } - _ = gui.closeConfirmationPrompt(g) - _ = gui.refreshCommits(g) - _ = gui.refreshStatus(g) - } - gui.refreshFiles(g) + gui.HandleCredentialsPopup(g, unamePassOpend, err) }() return nil } @@ -421,22 +404,7 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error unamePassOpend = true return gui.waitForPassUname(g, v, passOrUname) }) - if err != nil { - errMessage := err.Error() - if errMessage == "exit status 128" { - errMessage = gui.Tr.SLocalize("PassUnameWrong") - } - _ = gui.createErrorPanel(g, errMessage) - if unamePassOpend { - _ = g.DeleteView("credentials") - } - } else { - if unamePassOpend { - _ = g.DeleteView("credentials") - } - _ = gui.closeConfirmationPrompt(g) - _ = gui.refreshSidePanels(g) - } + gui.HandleCredentialsPopup(g, unamePassOpend, err) }() return nil } diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 1046fb32e..298e0be7f 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -309,6 +309,26 @@ func (gui *Gui) resizeCurrentPopupPanel(g *gocui.Gui) error { return nil } +// HandleCredentialsPopup handles the views after executing a command that might ask for credentials +func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr error) { + if cmdErr != nil { + errMessage := cmdErr.Error() + if errMessage == "exit status 128" { + errMessage = gui.Tr.SLocalize("PassUnameWrong") + } + _ = gui.createErrorPanel(g, errMessage) + if popupOpened { + _ = g.DeleteView("credentials") + } + } else { + if popupOpened { + _ = g.DeleteView("credentials") + } + _ = gui.closeConfirmationPrompt(g) + _ = gui.refreshSidePanels(g) + } +} + func (gui *Gui) resizePopupPanel(g *gocui.Gui, v *gocui.View) error { // If the confirmation panel is already displayed, just resize the width, // otherwise continue From 76e9582739c329fa657b327254bf010c560ac5e8 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 10 Dec 2018 13:45:03 +0100 Subject: [PATCH 072/118] Not always git fetch --- pkg/config/app_config.go | 22 +++++++++++++----- pkg/gui/gui.go | 20 +++++++++++------ pkg/gui/recent_repos_panel.go | 42 +++++++++++++---------------------- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 48cf2df35..18aa961bc 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -20,6 +20,7 @@ type AppConfig struct { BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""` UserConfig *viper.Viper AppState *AppState + IsNewRepo bool } // AppConfigurer interface allows individual app config structs to inherit Fields @@ -36,6 +37,8 @@ type AppConfigurer interface { WriteToUserConfig(string, string) error SaveAppState() error LoadAppState() error + SetIsNewRepo(bool) + GetIsNewRepo() bool } // NewAppConfig makes a new app config @@ -54,6 +57,7 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg BuildSource: buildSource, UserConfig: userConfig, AppState: &AppState{}, + IsNewRepo: false, } if err := appConfig.LoadAppState(); err != nil { @@ -63,6 +67,16 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg return appConfig, nil } +// GetIsNewRepo returns known repo boolean +func (c *AppConfig) GetIsNewRepo() bool { + return c.IsNewRepo +} + +// SetIsNewRepo set if the current repo is known +func (c *AppConfig) SetIsNewRepo(toSet bool) { + c.IsNewRepo = toSet +} + // GetDebug returns debug flag func (c *AppConfig) GetDebug() bool { return c.Debug @@ -153,7 +167,7 @@ func prepareConfigFile(filename string) (string, error) { } // LoadAndMergeFile Loads the config/state file, creating -// the file as an empty one if it does not exist +// the file has an empty one if it does not exist func LoadAndMergeFile(v *viper.Viper, filename string) error { configPath, err := prepareConfigFile(filename) if err != nil { @@ -236,16 +250,14 @@ confirmOnQuit: false // AppState stores data between runs of the app like when the last update check // was performed and which other repos have been checked out type AppState struct { - LastUpdateCheck int64 - RecentRepos []string - RecentPrivateRepos []string + LastUpdateCheck int64 + RecentRepos []string } func getDefaultAppState() []byte { return []byte(` lastUpdateCheck: 0 recentRepos: [] - recentPrivateRepos: [] `) } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index a8a393620..3345be560 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -73,7 +73,7 @@ type Gui struct { Updater *updates.Updater statusManager *statusManager credentials credentials - introAgree sync.WaitGroup + waitForIntro sync.WaitGroup } // for now the staging panel state, unlike the other panel states, is going to be @@ -387,6 +387,7 @@ func (gui *Gui) layout(g *gocui.Gui) error { if err := gui.updateRecentRepoList(); err != nil { return err } + gui.waitForIntro.Done() if _, err := gui.g.SetCurrentView(filesView.Name()); err != nil { return err @@ -422,16 +423,15 @@ func (gui *Gui) layout(g *gocui.Gui) error { // if you download humanlog and do tail -f development.log | humanlog // this will let you see these branches as prettified json // gui.Log.Info(utils.AsJson(gui.State.Branches[0:4])) - return gui.resizeCurrentPopupPanel(g) } 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 { - gui.introAgree.Done() + gui.waitForIntro.Done() return gui.Config.WriteToUserConfig("reporting", "on") }, func(g *gocui.Gui, v *gocui.View) error { - gui.introAgree.Done() + gui.waitForIntro.Done() return gui.Config.WriteToUserConfig("reporting", "off") }) } @@ -509,13 +509,19 @@ func (gui *Gui) Run() error { } if gui.Config.GetUserConfig().GetString("reporting") == "undetermined" { - gui.introAgree.Add(1) + gui.waitForIntro.Add(2) + } else { + gui.waitForIntro.Add(1) } go func() { + gui.waitForIntro.Wait() + isNew := gui.Config.GetIsNewRepo() + if !isNew { + time.After(60 * time.Second) + } _, err := gui.fetch(g, g.CurrentView(), false) - if err != nil && strings.Contains(err.Error(), "exit status 128") && gui.IsNewPrivateRepo() { - gui.introAgree.Wait() + if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew { _ = gui.createConfirmationPanel(g, g.CurrentView(), gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil) } else { gui.goEvery(g, time.Second*60, func(g *gocui.Gui) error { diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index 4ee990076..105d9b9cf 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -14,8 +14,8 @@ type recentRepo struct { path string } -// GetDisplayStrings is a function. -func (r *recentRepo) GetDisplayStrings() []string { +// GetDisplayStrings returns the path from a recent repo. +func (r recentRepo) GetDisplayStrings() []string { yellow := color.New(color.FgMagenta) base := filepath.Base(r.path) path := yellow.Sprint(r.path) @@ -26,14 +26,14 @@ 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} + recentRepos := make([]string, reposCount-1) + for i, repo := range recentRepoPaths[1:reposCount] { + recentRepos[i] = repo } handleMenuPress := func(index int) error { - repo := recentRepos[index] - if err := os.Chdir(repo.path); err != nil { + repoPath := recentRepos[index] + if err := os.Chdir(repoPath); err != nil { return err } newGitCommand, err := commands.NewGitCommand(gui.Log, gui.OSCommand, gui.Tr) @@ -55,34 +55,22 @@ func (gui *Gui) updateRecentRepoList() error { if err != nil { return err } - gui.Config.GetAppState().RecentRepos = newRecentReposList(recentRepos, currentRepo) + known, recentRepos := newRecentReposList(recentRepos, currentRepo) + gui.Config.SetIsNewRepo(known) + gui.Config.GetAppState().RecentRepos = recentRepos return gui.Config.SaveAppState() } -// IsNewPrivateRepo returns true if a private repo is never opend before in lazygit -func (gui *Gui) IsNewPrivateRepo() bool { - repos := gui.Config.GetAppState().RecentPrivateRepos - currentRepo, err := os.Getwd() - if err != nil { - return true - } - for _, repo := range repos { - if currentRepo == repo { - return false - } - } - gui.Config.GetAppState().RecentPrivateRepos = newRecentReposList(repos, currentRepo) - _ = gui.Config.SaveAppState() - return true -} - // newRecentReposList returns a new repo list with a new entry but only when it doesn't exist yet -func newRecentReposList(recentRepos []string, currentRepo string) []string { +func newRecentReposList(recentRepos []string, currentRepo string) (bool, []string) { + isNew := true newRepos := []string{currentRepo} for _, repo := range recentRepos { if repo != currentRepo { newRepos = append(newRepos, repo) + } else { + isNew = false } } - return newRepos + return isNew, newRepos } From e20d8366e1fb971dfb8d8cb2f8aebd17366f14a5 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 10 Dec 2018 14:21:00 +0100 Subject: [PATCH 073/118] Made gobot happy --- pkg/gui/recent_repos_panel.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index 105d9b9cf..326d35b1e 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -15,7 +15,7 @@ type recentRepo struct { } // GetDisplayStrings returns the path from a recent repo. -func (r recentRepo) GetDisplayStrings() []string { +func (r *recentRepo) GetDisplayStrings() []string { yellow := color.New(color.FgMagenta) base := filepath.Base(r.path) path := yellow.Sprint(r.path) @@ -26,14 +26,14 @@ 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([]string, reposCount-1) - for i, repo := range recentRepoPaths[1:reposCount] { - recentRepos[i] = repo + recentRepos := make([]*recentRepo, reposCount-1) + for i, path := range recentRepoPaths[1:reposCount] { + recentRepos[i] = &recentRepo{path: path} } handleMenuPress := func(index int) error { - repoPath := recentRepos[index] - if err := os.Chdir(repoPath); err != nil { + repo := recentRepos[index] + if err := os.Chdir(repo.path); err != nil { return err } newGitCommand, err := commands.NewGitCommand(gui.Log, gui.OSCommand, gui.Tr) From c71bcc64ed7e3a5dc921b14df53dbe9d2837a20a Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 12 Dec 2018 22:33:42 +1100 Subject: [PATCH 074/118] move fetch keybinding to files view to make way for branch-specific fetched --- pkg/gui/keybindings.go | 12 ++++++------ pkg/i18n/english.go | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 1823c2955..b996e2e95 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -219,6 +219,12 @@ func (gui *Gui) GetKeybindings() []*Binding { Handler: gui.handleSwitchToStagingPanel, Description: gui.Tr.SLocalize("StageLines"), KeyReadable: "enter", + }, { + ViewName: "files", + Key: 'f', + Modifier: gocui.ModNone, + Handler: gui.handleGitFetch, + Description: gui.Tr.SLocalize("fetch"), }, { ViewName: "main", Key: gocui.KeyEsc, @@ -304,12 +310,6 @@ func (gui *Gui) GetKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handleForceCheckout, Description: gui.Tr.SLocalize("forceCheckout"), - }, { - ViewName: "branches", - Key: 'f', - Modifier: gocui.ModNone, - Handler: gui.handleGitFetch, - Description: gui.Tr.SLocalize("fetch"), }, { ViewName: "branches", Key: 'n', diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 26186c6d9..15179ccbc 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -431,7 +431,7 @@ func addEnglish(i18nObject *i18n.Bundle) error { Other: `No automatic git fetch`, }, &i18n.Message{ ID: "NoAutomaticGitFetchBody", - Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run "git fetch" manually`, + Other: `Lazygit can't use "git fetch" in a private repo; use 'f' in the files panel to run "git fetch" manually`, }, &i18n.Message{ ID: "StageLines", Other: `stage individual hunks/lines`, From a26c15dafa7e8c80a56ce0f452137e0af16a883e Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 12 Dec 2018 22:34:20 +1100 Subject: [PATCH 075/118] some fixes for issues around the credentials panel --- pkg/commands/exec_live_default.go | 29 ++++++++-------------- pkg/gui/commit_message_panel.go | 9 +++++-- pkg/gui/gui.go | 18 ++++++++------ pkg/gui/view_helpers.go | 41 ++++++++++++++++--------------- 4 files changed, 49 insertions(+), 48 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 5fb84c81e..9f08b014b 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -30,42 +30,33 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s tty, err := pty.Start(cmd) + // go func() { + // _ = tty.Close() + // }() + if err != nil { return "", err } - stopAsking := make(chan struct{}) - var waitForBufio sync.WaitGroup waitForBufio.Add(1) - defer func() { - _ = tty.Close() - }() - go func() { scanner := bufio.NewScanner(tty) scanner.Split(scanWordsWithNewLines) for scanner.Scan() { - select { - case <-stopAsking: - // just do nothing - default: - toOutput := strings.Trim(scanner.Text(), " ") - cmdOutput = append(cmdOutput, toOutput) - toWrite := output(toOutput) - if len(toWrite) > 0 { - _, _ = tty.Write([]byte(toWrite + "\n")) - } + toOutput := strings.Trim(scanner.Text(), " ") + cmdOutput = append(cmdOutput, toOutput) + toWrite := output(toOutput) + if len(toWrite) > 0 { + _, _ = tty.Write([]byte(toWrite + "\n")) } } waitForBufio.Done() }() err = cmd.Wait() - go func() { - stopAsking <- struct{}{} - }() + tty.Close() if err != nil { waitForBufio.Wait() return strings.Join(cmdOutput, " "), err diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 4ba9a3d97..8f14cf6e1 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -103,7 +103,11 @@ func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { if err != nil { return err } - err = gui.switchFocus(g, v, gui.getFilesView(g)) + nextView, err := gui.g.View("confirmation") + if err != nil { + nextView = gui.getFilesView(g) + } + err = gui.switchFocus(g, nil, nextView) if err != nil { return err } @@ -115,8 +119,9 @@ func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { if err != nil { return err } + gui.credentials <- "-" - return gui.switchFocus(g, v, gui.getFilesView(g)) + return gui.switchFocus(g, nil, gui.getFilesView(g)) } func (gui *Gui) handlePushFocused(g *gocui.Gui, v *gocui.View) error { diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 3345be560..b0062dba8 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -457,15 +457,19 @@ func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canSskForCredentials bool) (u } func (gui *Gui) updateLoader(g *gocui.Gui) error { - if view, _ := g.View("confirmation"); view != nil { - content := gui.trimmedContent(view) - if strings.Contains(content, "...") { - staticContent := strings.Split(content, "...")[0] + "..." - if err := gui.renderString(g, "confirmation", staticContent+" "+utils.Loader()); err != nil { - return err + gui.g.Update(func(g *gocui.Gui) error { + if view, _ := g.View("confirmation"); view != nil { + content := gui.trimmedContent(view) + if strings.Contains(content, "...") { + staticContent := strings.Split(content, "...")[0] + "..." + if err := gui.synchronousRenderString(g, "confirmation", staticContent+" "+utils.Loader()); err != nil { + return err + } } } - } + return nil + }) + return nil } diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 298e0be7f..fa676a47d 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -222,22 +222,26 @@ func (gui *Gui) focusPoint(cx int, cy int, v *gocui.View) error { return nil } +func (gui *Gui) synchronousRenderString(g *gocui.Gui, viewName, s string) error { + v, err := g.View(viewName) + // just in case the view disappeared as this function was called, we'll + // silently return if it's not found + if err != nil { + return nil + } + v.Clear() + if err := v.SetOrigin(0, 0); err != nil { + return err + } + output := string(bom.Clean([]byte(s))) + output = utils.NormalizeLinefeeds(output) + fmt.Fprint(v, output) + return nil +} + func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error { g.Update(func(*gocui.Gui) error { - v, err := g.View(viewName) - // just in case the view disappeared as this function was called, we'll - // silently return if it's not found - if err != nil { - return nil - } - v.Clear() - if err := v.SetOrigin(0, 0); err != nil { - return err - } - output := string(bom.Clean([]byte(s))) - output = utils.NormalizeLinefeeds(output) - fmt.Fprint(v, output) - return nil + return gui.synchronousRenderString(gui.g, viewName, s) }) return nil } @@ -311,19 +315,16 @@ func (gui *Gui) resizeCurrentPopupPanel(g *gocui.Gui) error { // HandleCredentialsPopup handles the views after executing a command that might ask for credentials func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr error) { + if popupOpened { + _ = g.DeleteView("credentials") + } if cmdErr != nil { errMessage := cmdErr.Error() if errMessage == "exit status 128" { errMessage = gui.Tr.SLocalize("PassUnameWrong") } _ = gui.createErrorPanel(g, errMessage) - if popupOpened { - _ = g.DeleteView("credentials") - } } else { - if popupOpened { - _ = g.DeleteView("credentials") - } _ = gui.closeConfirmationPrompt(g) _ = gui.refreshSidePanels(g) } From 0577d3b97fcedaf02b5aaa19ac29bdd082599209 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 12 Dec 2018 21:08:53 +0100 Subject: [PATCH 076/118] Removed the username / password savety check This check is not realy needed because the change that it will show up a second time is so low that this is more work to check than the change it actualy might happen --- pkg/commands/os.go | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 037f6da2e..d40a60e46 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -71,27 +71,13 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err errMessage, err := c.RunCommandWithOutputLive(command, func(word string) string { ttyText = ttyText + " " + word - type Prompt struct { - pattern string - canAskFor bool - } - prompts := map[string]Prompt{ - "password": { - pattern: `Password\s*for\s*'.+':`, - canAskFor: true, - }, - "username": { - pattern: `Username\s*for\s*'.+':`, - canAskFor: true, - }, + prompts := map[string]string{ + "password": `Password\s*for\s*'.+':`, + "username": `Username\s*for\s*'.+':`, } - for askFor, prompt := range prompts { - if !prompt.canAskFor { - continue - } - if match, _ := regexp.MatchString(prompt.pattern, ttyText); match { - prompt.canAskFor = false + for askFor, pattern := range prompts { + if match, _ := regexp.MatchString(pattern, ttyText); match { ttyText = "" return ask(askFor) } From 5d038dfd334d4bdd767ac4db504a21160a013f79 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 12 Dec 2018 22:10:33 +0100 Subject: [PATCH 077/118] Removed the wired error handling --- pkg/commands/exec_live_default.go | 19 +++++++++++-------- pkg/commands/exec_live_win.go | 5 ++--- pkg/commands/os.go | 12 +++--------- pkg/gui/view_helpers.go | 2 +- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 9f08b014b..acc0bcf18 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -4,6 +4,7 @@ package commands import ( "bufio" + "errors" "os" "os/exec" "strings" @@ -19,8 +20,9 @@ import ( // As return of output you need to give a string that will be written to stdin // NOTE: If the return data is empty it won't written anything to stdin // NOTE: You don't have to include a enter in the return data this function will do that for you -func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { +func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { cmdOutput := []string{} + cmdOutputOffset := 0 splitCmd := str.ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) @@ -30,12 +32,8 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s tty, err := pty.Start(cmd) - // go func() { - // _ = tty.Close() - // }() - if err != nil { - return "", err + return err } var waitForBufio sync.WaitGroup @@ -49,6 +47,8 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s cmdOutput = append(cmdOutput, toOutput) toWrite := output(toOutput) if len(toWrite) > 0 { + // don't do -1 because the next value is the username / password + cmdOutputOffset = len(cmdOutput) _, _ = tty.Write([]byte(toWrite + "\n")) } } @@ -59,10 +59,13 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s tty.Close() if err != nil { waitForBufio.Wait() - return strings.Join(cmdOutput, " "), err + if len(cmdOutput) == cmdOutputOffset { + cmdOutputOffset-- + } + return errors.New(err.Error() + ", " + strings.Join(cmdOutput[cmdOutputOffset:], " ")) } - return errorMessage, nil + return nil } // scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines diff --git a/pkg/commands/exec_live_win.go b/pkg/commands/exec_live_win.go index d97274279..343e0dd9f 100644 --- a/pkg/commands/exec_live_win.go +++ b/pkg/commands/exec_live_win.go @@ -4,7 +4,6 @@ package commands // RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there // TODO: Remove this hack and replace it with a propper way to run commands live on windows -func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) (errorMessage string, codeError error) { - cmdOputput := c.RunCommand(command) - return cmdOputput.Error(), cmdOputput +func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { + return c.RunCommand(command) } diff --git a/pkg/commands/os.go b/pkg/commands/os.go index d40a60e46..827e6c420 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -59,7 +59,7 @@ func (c *OSCommand) RunCommandWithOutput(command string) (string, error) { } // RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper -func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) (errorMessage string, err error) { +func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) string) error { return RunCommandWithOutputLiveWrapper(c, command, output) } @@ -68,7 +68,7 @@ func (c *OSCommand) RunCommandWithOutputLive(command string, output func(string) // The ask argument will be "username" or "password" and expects the user's password or username back func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) error { ttyText := "" - errMessage, err := c.RunCommandWithOutputLive(command, func(word string) string { + errMessage := c.RunCommandWithOutputLive(command, func(word string) string { ttyText = ttyText + " " + word prompts := map[string]string{ @@ -85,13 +85,7 @@ func (c *OSCommand) DetectUnamePass(command string, ask func(string) string) err return "" }) - if err != nil { - if strings.Contains("exit status 128", err.Error()) { - errMessage = "exit status 128" - } - return errors.New(errMessage) - } - return nil + return errMessage } // RunCommand runs a command and just returns the error diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index fa676a47d..883773926 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -320,7 +320,7 @@ func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr er } if cmdErr != nil { errMessage := cmdErr.Error() - if errMessage == "exit status 128" { + if strings.Contains(errMessage, "exit status 128") { errMessage = gui.Tr.SLocalize("PassUnameWrong") } _ = gui.createErrorPanel(g, errMessage) From 0fca27d0221cba90afe2ac3d8dcb85a7dd6f1e8b Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 14 Dec 2018 13:40:29 +0100 Subject: [PATCH 078/118] Tried to fix circleci --- pkg/commands/git_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 7d0a503d7..7d29f6889 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1031,12 +1031,12 @@ func TestGitCommandPush(t *testing.T) { func(cmd string, args ...string) *exec.Cmd { assert.EqualValues(t, "git", cmd) assert.EqualValues(t, []string{"push", "-u", "origin", "test"}, args) - return exec.Command("test") }, false, func(err error) { - assert.Equal(t, "exit status 128", err.Error()) + + assert.Contains(t, "exit status 1", err.Error()) }, }, } From 70cd6700e7c9efa8fbdb2402d66deb7b6436f2f6 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 14 Dec 2018 13:43:13 +0100 Subject: [PATCH 079/118] Tried to fix circleci --- pkg/commands/git_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 7d29f6889..f599aeed4 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1010,7 +1010,7 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Equal(t, "exit status 128", err.Error()) + assert.Contains(t, "exit status 1", err.Error()) }, }, { @@ -1023,7 +1023,7 @@ func TestGitCommandPush(t *testing.T) { }, true, func(err error) { - assert.Equal(t, "exit status 128", err.Error()) + assert.Contains(t, "exit status 1", err.Error()) }, }, { @@ -1035,7 +1035,6 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Contains(t, "exit status 1", err.Error()) }, }, From af26b5f3e0779fb218bb77506fbcd7da964942fe Mon Sep 17 00:00:00 2001 From: mjarkk Date: Fri, 14 Dec 2018 13:45:43 +0100 Subject: [PATCH 080/118] Tried to fix circleci --- pkg/commands/git_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index f599aeed4..2413976fc 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1010,7 +1010,7 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Contains(t, "exit status 1", err.Error()) + assert.Contains(t, err.Error(), "exit status 1") }, }, { @@ -1023,7 +1023,7 @@ func TestGitCommandPush(t *testing.T) { }, true, func(err error) { - assert.Contains(t, "exit status 1", err.Error()) + assert.Contains(t, err.Error(), "exit status 1") }, }, { @@ -1035,7 +1035,7 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Contains(t, "exit status 1", err.Error()) + assert.Contains(t, err.Error(), "exit status 1") }, }, } From 4886b8350e0af56fd1d634c99e0f7502f23b9289 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 16 Dec 2018 17:05:34 +1100 Subject: [PATCH 081/118] always hide rather than delete the credentials view, and don't log on error in case there is a user password in the error --- pkg/commands/exec_live_default.go | 8 ++++---- pkg/commands/os.go | 2 +- pkg/gui/commit_message_panel.go | 8 ++------ pkg/gui/confirmation_panel.go | 32 ++++++++++++++++++++----------- pkg/gui/view_helpers.go | 7 ++++--- 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index acc0bcf18..5ee5bdf75 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -47,9 +47,9 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s cmdOutput = append(cmdOutput, toOutput) toWrite := output(toOutput) if len(toWrite) > 0 { - // don't do -1 because the next value is the username / password + // don't do len(cmdOutput)-1 because the next value is the username / password cmdOutputOffset = len(cmdOutput) - _, _ = tty.Write([]byte(toWrite + "\n")) + _, _ = tty.WriteString(toWrite + "\n") } } waitForBufio.Done() @@ -59,8 +59,8 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s tty.Close() if err != nil { waitForBufio.Wait() - if len(cmdOutput) == cmdOutputOffset { - cmdOutputOffset-- + if cmdOutputOffset > len(cmdOutput)-1 { + cmdOutputOffset = len(cmdOutput) - 1 } return errors.New(err.Error() + ", " + strings.Join(cmdOutput[cmdOutputOffset:], " ")) } diff --git a/pkg/commands/os.go b/pkg/commands/os.go index 827e6c420..ffc0516ad 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -217,7 +217,7 @@ func (c *OSCommand) CreateTempFile(filename, content string) (string, error) { return "", err } - if _, err := tmpfile.Write([]byte(content)); err != nil { + if _, err := tmpfile.WriteString(content); err != nil { c.Log.Error(err) return "", err } diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 8f14cf6e1..71f18e1f0 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -65,11 +65,7 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn credentialsView.Mask = '*' } g.Update(func(g *gocui.Gui) error { - _, err := g.SetViewOnTop("credentials") - if err != nil { - return err - } - err = gui.switchFocus(g, currentView, credentialsView) + err := gui.switchFocus(g, currentView, credentialsView) if err != nil { return err } @@ -124,7 +120,7 @@ func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { return gui.switchFocus(g, nil, gui.getFilesView(g)) } -func (gui *Gui) handlePushFocused(g *gocui.Gui, v *gocui.View) error { +func (gui *Gui) handleCredentialsViewFocused(g *gocui.Gui, v *gocui.View) error { if _, err := g.SetViewOnTop("credentials"); err != nil { return err } diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index 99455a30c..cdb01466a 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -89,6 +89,7 @@ func (gui *Gui) onNewPopupPanel() { _, _ = gui.g.SetViewOnBottom("credentials") } +// it is very important that within this function we never include the original prompt in any error messages, because it may contain e.g. a user password func (gui *Gui) createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error { gui.onNewPopupPanel() g.Update(func(g *gocui.Gui) error { @@ -138,18 +139,27 @@ func (gui *Gui) createMessagePanel(g *gocui.Gui, currentView *gocui.View, title, return gui.createConfirmationPanel(g, currentView, title, prompt, nil, nil) } -func (gui *Gui) createErrorPanel(g *gocui.Gui, message string) error { - go func() { - // when reporting is switched on this log call sometimes introduces - // a delay on the error panel popping up. Here I'm adding a second wait - // so that the error is logged while the user is reading the error message - time.Sleep(time.Second) - gui.Log.Error(message) - }() +// createSpecificErrorPanel allows you to create an error popup, specifying the +// view to be focused when the user closes the popup, and a boolean specifying +// whether we will log the error. If the message may include a user password, +// this function is to be used over the more generic createErrorPanel, with +// willLog set to false +func (gui *Gui) createSpecificErrorPanel(message string, nextView *gocui.View, willLog bool) error { + if willLog { + go func() { + // when reporting is switched on this log call sometimes introduces + // a delay on the error panel popping up. Here I'm adding a second wait + // so that the error is logged while the user is reading the error message + time.Sleep(time.Second) + gui.Log.Error(message) + }() + } - // gui.Log.WithField("staging", "staging").Info("creating confirmation panel") - currentView := g.CurrentView() colorFunction := color.New(color.FgRed).SprintFunc() coloredMessage := colorFunction(strings.TrimSpace(message)) - return gui.createConfirmationPanel(g, currentView, gui.Tr.SLocalize("Error"), coloredMessage, nil, nil) + return gui.createConfirmationPanel(gui.g, nextView, gui.Tr.SLocalize("Error"), coloredMessage, nil, nil) +} + +func (gui *Gui) createErrorPanel(g *gocui.Gui, message string) error { + return gui.createSpecificErrorPanel(message, g.CurrentView(), true) } diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 883773926..4befe1e0d 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -102,7 +102,7 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error { case "commitMessage": return gui.handleCommitFocused(g, v) case "credentials": - return gui.handlePushFocused(g, v) + return gui.handleCredentialsViewFocused(g, v) case "main": // TODO: pull this out into a 'view focused' function gui.refreshMergePanel(g) @@ -316,14 +316,15 @@ func (gui *Gui) resizeCurrentPopupPanel(g *gocui.Gui) error { // HandleCredentialsPopup handles the views after executing a command that might ask for credentials func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr error) { if popupOpened { - _ = g.DeleteView("credentials") + _, _ = gui.g.SetViewOnBottom("credentials") } if cmdErr != nil { errMessage := cmdErr.Error() if strings.Contains(errMessage, "exit status 128") { errMessage = gui.Tr.SLocalize("PassUnameWrong") } - _ = gui.createErrorPanel(g, errMessage) + // we are not logging this error because it may contain a password + _ = gui.createSpecificErrorPanel(errMessage, gui.getFilesView(gui.g), false) } else { _ = gui.closeConfirmationPrompt(g) _ = gui.refreshSidePanels(g) From c56b303b2983e313261db6e89891058fa63739ac Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 16 Dec 2018 17:55:37 +1100 Subject: [PATCH 082/118] add pre-hook to test credentials panel --- test/hooks/pre-push | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/hooks/pre-push diff --git a/test/hooks/pre-push b/test/hooks/pre-push new file mode 100644 index 000000000..156b9664c --- /dev/null +++ b/test/hooks/pre-push @@ -0,0 +1,22 @@ +#!/bin/bash + +# test pre-push hook for testing the lazygit credentials view +# +# to enable, use: +# chmod +x .git/hooks/pre-push +# +# this will hang if you're using git from the command line, so only enable this +# when you are testing the credentials view in lazygit + +exec < /dev/tty + +echo -n "Username for 'github': " +read username + +echo -n "Password for 'github': " +read password + +# echo "failed" +# exit 1 + +exit 0 \ No newline at end of file From f5f726e9c46450fee60330c1c453d33d1566e106 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Mon, 17 Dec 2018 08:58:09 +0100 Subject: [PATCH 083/118] A try to hide the password from the error output --- pkg/commands/exec_live_default.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 5ee5bdf75..37e27625a 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -36,6 +36,8 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s return err } + lastWritten := "" + var waitForBufio sync.WaitGroup waitForBufio.Add(1) @@ -47,6 +49,8 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s cmdOutput = append(cmdOutput, toOutput) toWrite := output(toOutput) if len(toWrite) > 0 { + toWriteSplit := strings.Split(toWrite, " ") + lastWritten = toWriteSplit[len(toWriteSplit)-1] // don't do len(cmdOutput)-1 because the next value is the username / password cmdOutputOffset = len(cmdOutput) _, _ = tty.WriteString(toWrite + "\n") @@ -59,6 +63,11 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s tty.Close() if err != nil { waitForBufio.Wait() + for i, item := range cmdOutput { + if lastWritten == item && cmdOutputOffset < i { + cmdOutputOffset = i + 1 + } + } if cmdOutputOffset > len(cmdOutput)-1 { cmdOutputOffset = len(cmdOutput) - 1 } From 319064f040e028d75480ac0093225f23e81ec635 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 21:25:28 +1100 Subject: [PATCH 084/118] switch to our own fork of pty which lets us set our own stdout and stderr --- Gopkg.lock | 9 + vendor/github.com/flynn/go-shlex/COPYING | 202 ----- vendor/github.com/flynn/go-shlex/Makefile | 21 - vendor/github.com/flynn/go-shlex/README.md | 2 - vendor/github.com/flynn/go-shlex/shlex.go | 457 ------------ .../github.com/flynn/go-shlex/shlex_test.go | 162 ---- .../{kr => jesseduffield}/pty/License | 0 .../{kr => jesseduffield}/pty/doc.go | 0 .../{kr => jesseduffield}/pty/ioctl.go | 0 .../{kr => jesseduffield}/pty/ioctl_bsd.go | 0 .../{kr => jesseduffield}/pty/pty_darwin.go | 0 .../pty/pty_dragonfly.go | 0 .../{kr => jesseduffield}/pty/pty_freebsd.go | 0 .../{kr => jesseduffield}/pty/pty_linux.go | 0 .../{kr => jesseduffield}/pty/pty_openbsd.go | 0 .../pty/pty_unsupported.go | 0 .../{kr => jesseduffield}/pty/run.go | 16 + .../{kr => jesseduffield}/pty/types.go | 0 .../pty/types_dragonfly.go | 0 .../pty/types_freebsd.go | 0 .../pty/types_openbsd.go | 0 .../{kr => jesseduffield}/pty/util.go | 0 .../{kr => jesseduffield}/pty/ztypes_386.go | 0 .../{kr => jesseduffield}/pty/ztypes_amd64.go | 0 .../{kr => jesseduffield}/pty/ztypes_arm.go | 0 .../{kr => jesseduffield}/pty/ztypes_arm64.go | 0 .../pty/ztypes_dragonfly_amd64.go | 0 .../pty/ztypes_freebsd_386.go | 0 .../pty/ztypes_freebsd_amd64.go | 0 .../pty/ztypes_freebsd_arm.go | 0 .../{kr => jesseduffield}/pty/ztypes_mipsx.go | 0 .../pty/ztypes_openbsd_386.go | 0 .../pty/ztypes_openbsd_amd64.go | 0 .../{kr => jesseduffield}/pty/ztypes_ppc64.go | 0 .../pty/ztypes_ppc64le.go | 0 .../{kr => jesseduffield}/pty/ztypes_s390x.go | 0 vendor/github.com/kr/pty/README.md | 100 --- vendor/github.com/kr/pty/mktypes.bash | 19 - vendor/github.com/mgutz/str/CREDITS | 5 - vendor/github.com/mgutz/str/README.md | 649 ---------------- vendor/github.com/mgutz/str/VERSION | 1 - vendor/github.com/mgutz/str/str_test.go | 696 ------------------ vendor/vendor.json | 25 - 43 files changed, 25 insertions(+), 2339 deletions(-) delete mode 100644 vendor/github.com/flynn/go-shlex/COPYING delete mode 100644 vendor/github.com/flynn/go-shlex/Makefile delete mode 100644 vendor/github.com/flynn/go-shlex/README.md delete mode 100644 vendor/github.com/flynn/go-shlex/shlex.go delete mode 100644 vendor/github.com/flynn/go-shlex/shlex_test.go rename vendor/github.com/{kr => jesseduffield}/pty/License (100%) rename vendor/github.com/{kr => jesseduffield}/pty/doc.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ioctl.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ioctl_bsd.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/pty_darwin.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/pty_dragonfly.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/pty_freebsd.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/pty_linux.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/pty_openbsd.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/pty_unsupported.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/run.go (58%) rename vendor/github.com/{kr => jesseduffield}/pty/types.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/types_dragonfly.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/types_freebsd.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/types_openbsd.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/util.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_386.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_amd64.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_arm.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_arm64.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_dragonfly_amd64.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_freebsd_386.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_freebsd_amd64.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_freebsd_arm.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_mipsx.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_openbsd_386.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_openbsd_amd64.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_ppc64.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_ppc64le.go (100%) rename vendor/github.com/{kr => jesseduffield}/pty/ztypes_s390x.go (100%) delete mode 100644 vendor/github.com/kr/pty/README.md delete mode 100755 vendor/github.com/kr/pty/mktypes.bash delete mode 100644 vendor/github.com/mgutz/str/CREDITS delete mode 100644 vendor/github.com/mgutz/str/README.md delete mode 100644 vendor/github.com/mgutz/str/VERSION delete mode 100644 vendor/github.com/mgutz/str/str_test.go delete mode 100644 vendor/vendor.json diff --git a/Gopkg.lock b/Gopkg.lock index 4b84d6537..f5ef4c76c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -195,6 +195,14 @@ pruneopts = "NUT" revision = "03e26ff3f1de2c1bc2205113c3aba661312eee00" +[[projects]] + digest = "1:15416fd2b58d5b2dfa0bc99185cdff3a8f49e73e022291891cb8d5d820d9760d" + name = "github.com/jesseduffield/pty" + packages = ["."] + pruneopts = "NUT" + revision = "db8e3cd836b82e82e0a9c8edc6896967dd31374f" + version = "v1.1.3" + [[projects]] branch = "master" digest = "1:3ab130f65766f5b7cc944d557df31c6a007ec017151705ec1e1b8719f2689021" @@ -616,6 +624,7 @@ "github.com/heroku/rollrus", "github.com/jesseduffield/go-getter", "github.com/jesseduffield/gocui", + "github.com/jesseduffield/pty", "github.com/kardianos/osext", "github.com/mgutz/str", "github.com/nicksnyder/go-i18n/v2/i18n", diff --git a/vendor/github.com/flynn/go-shlex/COPYING b/vendor/github.com/flynn/go-shlex/COPYING deleted file mode 100644 index d64569567..000000000 --- a/vendor/github.com/flynn/go-shlex/COPYING +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/flynn/go-shlex/Makefile b/vendor/github.com/flynn/go-shlex/Makefile deleted file mode 100644 index 038d9a489..000000000 --- a/vendor/github.com/flynn/go-shlex/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2011 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -include $(GOROOT)/src/Make.inc - -TARG=shlex -GOFILES=\ - shlex.go\ - -include $(GOROOT)/src/Make.pkg diff --git a/vendor/github.com/flynn/go-shlex/README.md b/vendor/github.com/flynn/go-shlex/README.md deleted file mode 100644 index c86bcc066..000000000 --- a/vendor/github.com/flynn/go-shlex/README.md +++ /dev/null @@ -1,2 +0,0 @@ -go-shlex is a simple lexer for go that supports shell-style quoting, -commenting, and escaping. diff --git a/vendor/github.com/flynn/go-shlex/shlex.go b/vendor/github.com/flynn/go-shlex/shlex.go deleted file mode 100644 index 7aeace801..000000000 --- a/vendor/github.com/flynn/go-shlex/shlex.go +++ /dev/null @@ -1,457 +0,0 @@ -/* -Copyright 2012 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package shlex - -/* -Package shlex implements a simple lexer which splits input in to tokens using -shell-style rules for quoting and commenting. -*/ -import ( - "bufio" - "errors" - "fmt" - "io" - "strings" -) - -/* -A TokenType is a top-level token; a word, space, comment, unknown. -*/ -type TokenType int - -/* -A RuneTokenType is the type of a UTF-8 character; a character, quote, space, escape. -*/ -type RuneTokenType int - -type lexerState int - -type Token struct { - tokenType TokenType - value string -} - -/* -Two tokens are equal if both their types and values are equal. A nil token can -never equal another token. -*/ -func (a *Token) Equal(b *Token) bool { - if a == nil || b == nil { - return false - } - if a.tokenType != b.tokenType { - return false - } - return a.value == b.value -} - -const ( - RUNE_CHAR string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-,/@$*()+=><:;&^%~|!?[]{}" - RUNE_SPACE string = " \t\r\n" - RUNE_ESCAPING_QUOTE string = "\"" - RUNE_NONESCAPING_QUOTE string = "'" - RUNE_ESCAPE = "\\" - RUNE_COMMENT = "#" - - RUNETOKEN_UNKNOWN RuneTokenType = 0 - RUNETOKEN_CHAR RuneTokenType = 1 - RUNETOKEN_SPACE RuneTokenType = 2 - RUNETOKEN_ESCAPING_QUOTE RuneTokenType = 3 - RUNETOKEN_NONESCAPING_QUOTE RuneTokenType = 4 - RUNETOKEN_ESCAPE RuneTokenType = 5 - RUNETOKEN_COMMENT RuneTokenType = 6 - RUNETOKEN_EOF RuneTokenType = 7 - - TOKEN_UNKNOWN TokenType = 0 - TOKEN_WORD TokenType = 1 - TOKEN_SPACE TokenType = 2 - TOKEN_COMMENT TokenType = 3 - - STATE_START lexerState = 0 - STATE_INWORD lexerState = 1 - STATE_ESCAPING lexerState = 2 - STATE_ESCAPING_QUOTED lexerState = 3 - STATE_QUOTED_ESCAPING lexerState = 4 - STATE_QUOTED lexerState = 5 - STATE_COMMENT lexerState = 6 - - INITIAL_TOKEN_CAPACITY int = 100 -) - -/* -A type for classifying characters. This allows for different sorts of -classifiers - those accepting extended non-ascii chars, or strict posix -compatibility, for example. -*/ -type TokenClassifier struct { - typeMap map[int32]RuneTokenType -} - -func addRuneClass(typeMap *map[int32]RuneTokenType, runes string, tokenType RuneTokenType) { - for _, rune := range runes { - (*typeMap)[int32(rune)] = tokenType - } -} - -/* -Create a new classifier for basic ASCII characters. -*/ -func NewDefaultClassifier() *TokenClassifier { - typeMap := map[int32]RuneTokenType{} - addRuneClass(&typeMap, RUNE_CHAR, RUNETOKEN_CHAR) - addRuneClass(&typeMap, RUNE_SPACE, RUNETOKEN_SPACE) - addRuneClass(&typeMap, RUNE_ESCAPING_QUOTE, RUNETOKEN_ESCAPING_QUOTE) - addRuneClass(&typeMap, RUNE_NONESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE) - addRuneClass(&typeMap, RUNE_ESCAPE, RUNETOKEN_ESCAPE) - addRuneClass(&typeMap, RUNE_COMMENT, RUNETOKEN_COMMENT) - return &TokenClassifier{ - typeMap: typeMap} -} - -func (classifier *TokenClassifier) ClassifyRune(rune int32) RuneTokenType { - return classifier.typeMap[rune] -} - -/* -A type for turning an input stream in to a sequence of strings. Whitespace and -comments are skipped. -*/ -type Lexer struct { - tokenizer *Tokenizer -} - -/* -Create a new lexer. -*/ -func NewLexer(r io.Reader) (*Lexer, error) { - - tokenizer, err := NewTokenizer(r) - if err != nil { - return nil, err - } - lexer := &Lexer{tokenizer: tokenizer} - return lexer, nil -} - -/* -Return the next word, and an error value. If there are no more words, the error -will be io.EOF. -*/ -func (l *Lexer) NextWord() (string, error) { - var token *Token - var err error - for { - token, err = l.tokenizer.NextToken() - if err != nil { - return "", err - } - switch token.tokenType { - case TOKEN_WORD: - { - return token.value, nil - } - case TOKEN_COMMENT: - { - // skip comments - } - default: - { - panic(fmt.Sprintf("Unknown token type: %v", token.tokenType)) - } - } - } - return "", io.EOF -} - -/* -A type for turning an input stream in to a sequence of typed tokens. -*/ -type Tokenizer struct { - input *bufio.Reader - classifier *TokenClassifier -} - -/* -Create a new tokenizer. -*/ -func NewTokenizer(r io.Reader) (*Tokenizer, error) { - input := bufio.NewReader(r) - classifier := NewDefaultClassifier() - tokenizer := &Tokenizer{ - input: input, - classifier: classifier} - return tokenizer, nil -} - -/* -Scan the stream for the next token. - -This uses an internal state machine. It will panic if it encounters a character -which it does not know how to handle. -*/ -func (t *Tokenizer) scanStream() (*Token, error) { - state := STATE_START - var tokenType TokenType - value := make([]int32, 0, INITIAL_TOKEN_CAPACITY) - var ( - nextRune int32 - nextRuneType RuneTokenType - err error - ) -SCAN: - for { - nextRune, _, err = t.input.ReadRune() - nextRuneType = t.classifier.ClassifyRune(nextRune) - if err != nil { - if err == io.EOF { - nextRuneType = RUNETOKEN_EOF - err = nil - } else { - return nil, err - } - } - switch state { - case STATE_START: // no runes read yet - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - return nil, io.EOF - } - case RUNETOKEN_CHAR: - { - tokenType = TOKEN_WORD - value = append(value, nextRune) - state = STATE_INWORD - } - case RUNETOKEN_SPACE: - { - } - case RUNETOKEN_ESCAPING_QUOTE: - { - tokenType = TOKEN_WORD - state = STATE_QUOTED_ESCAPING - } - case RUNETOKEN_NONESCAPING_QUOTE: - { - tokenType = TOKEN_WORD - state = STATE_QUOTED - } - case RUNETOKEN_ESCAPE: - { - tokenType = TOKEN_WORD - state = STATE_ESCAPING - } - case RUNETOKEN_COMMENT: - { - tokenType = TOKEN_COMMENT - state = STATE_COMMENT - } - default: - { - return nil, errors.New(fmt.Sprintf("Unknown rune: %v", nextRune)) - } - } - } - case STATE_INWORD: // in a regular word - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_COMMENT: - { - value = append(value, nextRune) - } - case RUNETOKEN_SPACE: - { - t.input.UnreadRune() - break SCAN - } - case RUNETOKEN_ESCAPING_QUOTE: - { - state = STATE_QUOTED_ESCAPING - } - case RUNETOKEN_NONESCAPING_QUOTE: - { - state = STATE_QUOTED - } - case RUNETOKEN_ESCAPE: - { - state = STATE_ESCAPING - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_ESCAPING: // the next rune after an escape character - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - err = errors.New("EOF found after escape character") - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: - { - state = STATE_INWORD - value = append(value, nextRune) - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_ESCAPING_QUOTED: // the next rune after an escape character, in double quotes - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - err = errors.New("EOF found after escape character") - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: - { - state = STATE_QUOTED_ESCAPING - value = append(value, nextRune) - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_QUOTED_ESCAPING: // in escaping double quotes - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - err = errors.New("EOF found when expecting closing quote.") - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_COMMENT: - { - value = append(value, nextRune) - } - case RUNETOKEN_ESCAPING_QUOTE: - { - state = STATE_INWORD - } - case RUNETOKEN_ESCAPE: - { - state = STATE_ESCAPING_QUOTED - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_QUOTED: // in non-escaping single quotes - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - err = errors.New("EOF found when expecting closing quote.") - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: - { - value = append(value, nextRune) - } - case RUNETOKEN_NONESCAPING_QUOTE: - { - state = STATE_INWORD - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_COMMENT: - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT, RUNETOKEN_NONESCAPING_QUOTE: - { - value = append(value, nextRune) - } - case RUNETOKEN_SPACE: - { - if nextRune == '\n' { - state = STATE_START - break SCAN - } else { - value = append(value, nextRune) - } - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - default: - { - panic(fmt.Sprintf("Unexpected state: %v", state)) - } - } - } - token := &Token{ - tokenType: tokenType, - value: string(value)} - return token, err -} - -/* -Return the next token in the stream, and an error value. If there are no more -tokens available, the error value will be io.EOF. -*/ -func (t *Tokenizer) NextToken() (*Token, error) { - return t.scanStream() -} - -/* -Split a string in to a slice of strings, based upon shell-style rules for -quoting, escaping, and spaces. -*/ -func Split(s string) ([]string, error) { - l, err := NewLexer(strings.NewReader(s)) - if err != nil { - return nil, err - } - subStrings := []string{} - for { - word, err := l.NextWord() - if err != nil { - if err == io.EOF { - return subStrings, nil - } - return subStrings, err - } - subStrings = append(subStrings, word) - } - return subStrings, nil -} diff --git a/vendor/github.com/flynn/go-shlex/shlex_test.go b/vendor/github.com/flynn/go-shlex/shlex_test.go deleted file mode 100644 index 7551f7c59..000000000 --- a/vendor/github.com/flynn/go-shlex/shlex_test.go +++ /dev/null @@ -1,162 +0,0 @@ -/* -Copyright 2012 Google Inc. All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package shlex - -import ( - "strings" - "testing" -) - -func checkError(err error, t *testing.T) { - if err != nil { - t.Error(err) - } -} - -func TestClassifier(t *testing.T) { - classifier := NewDefaultClassifier() - runeTests := map[int32]RuneTokenType{ - 'a': RUNETOKEN_CHAR, - ' ': RUNETOKEN_SPACE, - '"': RUNETOKEN_ESCAPING_QUOTE, - '\'': RUNETOKEN_NONESCAPING_QUOTE, - '#': RUNETOKEN_COMMENT} - for rune, expectedType := range runeTests { - foundType := classifier.ClassifyRune(rune) - if foundType != expectedType { - t.Logf("Expected type: %v for rune '%c'(%v). Found type: %v.", expectedType, rune, rune, foundType) - t.Fail() - } - } -} - -func TestTokenizer(t *testing.T) { - testInput := strings.NewReader("one two \"three four\" \"five \\\"six\\\"\" seven#eight # nine # ten\n eleven") - expectedTokens := []*Token{ - &Token{ - tokenType: TOKEN_WORD, - value: "one"}, - &Token{ - tokenType: TOKEN_WORD, - value: "two"}, - &Token{ - tokenType: TOKEN_WORD, - value: "three four"}, - &Token{ - tokenType: TOKEN_WORD, - value: "five \"six\""}, - &Token{ - tokenType: TOKEN_WORD, - value: "seven#eight"}, - &Token{ - tokenType: TOKEN_COMMENT, - value: " nine # ten"}, - &Token{ - tokenType: TOKEN_WORD, - value: "eleven"}} - - tokenizer, err := NewTokenizer(testInput) - checkError(err, t) - for _, expectedToken := range expectedTokens { - foundToken, err := tokenizer.NextToken() - checkError(err, t) - if !foundToken.Equal(expectedToken) { - t.Error("Expected token:", expectedToken, ". Found:", foundToken) - } - } -} - -func TestLexer(t *testing.T) { - testInput := strings.NewReader("one") - expectedWord := "one" - lexer, err := NewLexer(testInput) - checkError(err, t) - foundWord, err := lexer.NextWord() - checkError(err, t) - if expectedWord != foundWord { - t.Error("Expected word:", expectedWord, ". Found:", foundWord) - } -} - -func TestSplitSimple(t *testing.T) { - testInput := "one two three" - expectedOutput := []string{"one", "two", "three"} - foundOutput, err := Split(testInput) - if err != nil { - t.Error("Split returned error:", err) - } - if len(expectedOutput) != len(foundOutput) { - t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") - } - for i := range foundOutput { - if foundOutput[i] != expectedOutput[i] { - t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) - } - } -} - -func TestSplitEscapingQuotes(t *testing.T) { - testInput := "one \"два ${three}\" four" - expectedOutput := []string{"one", "два ${three}", "four"} - foundOutput, err := Split(testInput) - if err != nil { - t.Error("Split returned error:", err) - } - if len(expectedOutput) != len(foundOutput) { - t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") - } - for i := range foundOutput { - if foundOutput[i] != expectedOutput[i] { - t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) - } - } -} - -func TestGlobbingExpressions(t *testing.T) { - testInput := "onefile *file one?ile onefil[de]" - expectedOutput := []string{"onefile", "*file", "one?ile", "onefil[de]"} - foundOutput, err := Split(testInput) - if err != nil { - t.Error("Split returned error", err) - } - if len(expectedOutput) != len(foundOutput) { - t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") - } - for i := range foundOutput { - if foundOutput[i] != expectedOutput[i] { - t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) - } - } - -} - -func TestSplitNonEscapingQuotes(t *testing.T) { - testInput := "one 'два ${three}' four" - expectedOutput := []string{"one", "два ${three}", "four"} - foundOutput, err := Split(testInput) - if err != nil { - t.Error("Split returned error:", err) - } - if len(expectedOutput) != len(foundOutput) { - t.Error("Split expected:", len(expectedOutput), "results. Found:", len(foundOutput), "results") - } - for i := range foundOutput { - if foundOutput[i] != expectedOutput[i] { - t.Error("Item:", i, "(", foundOutput[i], ") differs from the expected value:", expectedOutput[i]) - } - } -} diff --git a/vendor/github.com/kr/pty/License b/vendor/github.com/jesseduffield/pty/License similarity index 100% rename from vendor/github.com/kr/pty/License rename to vendor/github.com/jesseduffield/pty/License diff --git a/vendor/github.com/kr/pty/doc.go b/vendor/github.com/jesseduffield/pty/doc.go similarity index 100% rename from vendor/github.com/kr/pty/doc.go rename to vendor/github.com/jesseduffield/pty/doc.go diff --git a/vendor/github.com/kr/pty/ioctl.go b/vendor/github.com/jesseduffield/pty/ioctl.go similarity index 100% rename from vendor/github.com/kr/pty/ioctl.go rename to vendor/github.com/jesseduffield/pty/ioctl.go diff --git a/vendor/github.com/kr/pty/ioctl_bsd.go b/vendor/github.com/jesseduffield/pty/ioctl_bsd.go similarity index 100% rename from vendor/github.com/kr/pty/ioctl_bsd.go rename to vendor/github.com/jesseduffield/pty/ioctl_bsd.go diff --git a/vendor/github.com/kr/pty/pty_darwin.go b/vendor/github.com/jesseduffield/pty/pty_darwin.go similarity index 100% rename from vendor/github.com/kr/pty/pty_darwin.go rename to vendor/github.com/jesseduffield/pty/pty_darwin.go diff --git a/vendor/github.com/kr/pty/pty_dragonfly.go b/vendor/github.com/jesseduffield/pty/pty_dragonfly.go similarity index 100% rename from vendor/github.com/kr/pty/pty_dragonfly.go rename to vendor/github.com/jesseduffield/pty/pty_dragonfly.go diff --git a/vendor/github.com/kr/pty/pty_freebsd.go b/vendor/github.com/jesseduffield/pty/pty_freebsd.go similarity index 100% rename from vendor/github.com/kr/pty/pty_freebsd.go rename to vendor/github.com/jesseduffield/pty/pty_freebsd.go diff --git a/vendor/github.com/kr/pty/pty_linux.go b/vendor/github.com/jesseduffield/pty/pty_linux.go similarity index 100% rename from vendor/github.com/kr/pty/pty_linux.go rename to vendor/github.com/jesseduffield/pty/pty_linux.go diff --git a/vendor/github.com/kr/pty/pty_openbsd.go b/vendor/github.com/jesseduffield/pty/pty_openbsd.go similarity index 100% rename from vendor/github.com/kr/pty/pty_openbsd.go rename to vendor/github.com/jesseduffield/pty/pty_openbsd.go diff --git a/vendor/github.com/kr/pty/pty_unsupported.go b/vendor/github.com/jesseduffield/pty/pty_unsupported.go similarity index 100% rename from vendor/github.com/kr/pty/pty_unsupported.go rename to vendor/github.com/jesseduffield/pty/pty_unsupported.go diff --git a/vendor/github.com/kr/pty/run.go b/vendor/github.com/jesseduffield/pty/run.go similarity index 58% rename from vendor/github.com/kr/pty/run.go rename to vendor/github.com/jesseduffield/pty/run.go index baecca8af..2d7f96119 100644 --- a/vendor/github.com/kr/pty/run.go +++ b/vendor/github.com/jesseduffield/pty/run.go @@ -12,11 +12,27 @@ import ( // and c.Stderr, calls c.Start, and returns the File of the tty's // corresponding pty. func Start(c *exec.Cmd) (pty *os.File, err error) { + return StartWithSize(c, nil) +} + +// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout, +// and c.Stderr, calls c.Start, and returns the File of the tty's +// corresponding pty. +// +// This will resize the pty to the specified size before starting the command +func StartWithSize(c *exec.Cmd, sz *Winsize) (pty *os.File, err error) { pty, tty, err := Open() if err != nil { return nil, err } defer tty.Close() + if sz != nil { + err = Setsize(pty, sz) + if err != nil { + pty.Close() + return nil, err + } + } c.Stdout = tty c.Stdin = tty c.Stderr = tty diff --git a/vendor/github.com/kr/pty/types.go b/vendor/github.com/jesseduffield/pty/types.go similarity index 100% rename from vendor/github.com/kr/pty/types.go rename to vendor/github.com/jesseduffield/pty/types.go diff --git a/vendor/github.com/kr/pty/types_dragonfly.go b/vendor/github.com/jesseduffield/pty/types_dragonfly.go similarity index 100% rename from vendor/github.com/kr/pty/types_dragonfly.go rename to vendor/github.com/jesseduffield/pty/types_dragonfly.go diff --git a/vendor/github.com/kr/pty/types_freebsd.go b/vendor/github.com/jesseduffield/pty/types_freebsd.go similarity index 100% rename from vendor/github.com/kr/pty/types_freebsd.go rename to vendor/github.com/jesseduffield/pty/types_freebsd.go diff --git a/vendor/github.com/kr/pty/types_openbsd.go b/vendor/github.com/jesseduffield/pty/types_openbsd.go similarity index 100% rename from vendor/github.com/kr/pty/types_openbsd.go rename to vendor/github.com/jesseduffield/pty/types_openbsd.go diff --git a/vendor/github.com/kr/pty/util.go b/vendor/github.com/jesseduffield/pty/util.go similarity index 100% rename from vendor/github.com/kr/pty/util.go rename to vendor/github.com/jesseduffield/pty/util.go diff --git a/vendor/github.com/kr/pty/ztypes_386.go b/vendor/github.com/jesseduffield/pty/ztypes_386.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_386.go rename to vendor/github.com/jesseduffield/pty/ztypes_386.go diff --git a/vendor/github.com/kr/pty/ztypes_amd64.go b/vendor/github.com/jesseduffield/pty/ztypes_amd64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_amd64.go rename to vendor/github.com/jesseduffield/pty/ztypes_amd64.go diff --git a/vendor/github.com/kr/pty/ztypes_arm.go b/vendor/github.com/jesseduffield/pty/ztypes_arm.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_arm.go rename to vendor/github.com/jesseduffield/pty/ztypes_arm.go diff --git a/vendor/github.com/kr/pty/ztypes_arm64.go b/vendor/github.com/jesseduffield/pty/ztypes_arm64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_arm64.go rename to vendor/github.com/jesseduffield/pty/ztypes_arm64.go diff --git a/vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go b/vendor/github.com/jesseduffield/pty/ztypes_dragonfly_amd64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_dragonfly_amd64.go rename to vendor/github.com/jesseduffield/pty/ztypes_dragonfly_amd64.go diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_386.go b/vendor/github.com/jesseduffield/pty/ztypes_freebsd_386.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_freebsd_386.go rename to vendor/github.com/jesseduffield/pty/ztypes_freebsd_386.go diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go b/vendor/github.com/jesseduffield/pty/ztypes_freebsd_amd64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_freebsd_amd64.go rename to vendor/github.com/jesseduffield/pty/ztypes_freebsd_amd64.go diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_arm.go b/vendor/github.com/jesseduffield/pty/ztypes_freebsd_arm.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_freebsd_arm.go rename to vendor/github.com/jesseduffield/pty/ztypes_freebsd_arm.go diff --git a/vendor/github.com/kr/pty/ztypes_mipsx.go b/vendor/github.com/jesseduffield/pty/ztypes_mipsx.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_mipsx.go rename to vendor/github.com/jesseduffield/pty/ztypes_mipsx.go diff --git a/vendor/github.com/kr/pty/ztypes_openbsd_386.go b/vendor/github.com/jesseduffield/pty/ztypes_openbsd_386.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_openbsd_386.go rename to vendor/github.com/jesseduffield/pty/ztypes_openbsd_386.go diff --git a/vendor/github.com/kr/pty/ztypes_openbsd_amd64.go b/vendor/github.com/jesseduffield/pty/ztypes_openbsd_amd64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_openbsd_amd64.go rename to vendor/github.com/jesseduffield/pty/ztypes_openbsd_amd64.go diff --git a/vendor/github.com/kr/pty/ztypes_ppc64.go b/vendor/github.com/jesseduffield/pty/ztypes_ppc64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_ppc64.go rename to vendor/github.com/jesseduffield/pty/ztypes_ppc64.go diff --git a/vendor/github.com/kr/pty/ztypes_ppc64le.go b/vendor/github.com/jesseduffield/pty/ztypes_ppc64le.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_ppc64le.go rename to vendor/github.com/jesseduffield/pty/ztypes_ppc64le.go diff --git a/vendor/github.com/kr/pty/ztypes_s390x.go b/vendor/github.com/jesseduffield/pty/ztypes_s390x.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_s390x.go rename to vendor/github.com/jesseduffield/pty/ztypes_s390x.go diff --git a/vendor/github.com/kr/pty/README.md b/vendor/github.com/kr/pty/README.md deleted file mode 100644 index f9bb002e0..000000000 --- a/vendor/github.com/kr/pty/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# pty - -Pty is a Go package for using unix pseudo-terminals. - -## Install - - go get github.com/kr/pty - -## Example - -### Command - -```go -package main - -import ( - "github.com/kr/pty" - "io" - "os" - "os/exec" -) - -func main() { - c := exec.Command("grep", "--color=auto", "bar") - f, err := pty.Start(c) - if err != nil { - panic(err) - } - - go func() { - f.Write([]byte("foo\n")) - f.Write([]byte("bar\n")) - f.Write([]byte("baz\n")) - f.Write([]byte{4}) // EOT - }() - io.Copy(os.Stdout, f) -} -``` - -### Shell - -```go -package main - -import ( - "io" - "log" - "os" - "os/exec" - "os/signal" - "syscall" - - "github.com/kr/pty" - "golang.org/x/crypto/ssh/terminal" -) - -func test() error { - // Create arbitrary command. - c := exec.Command("bash") - - // Start the command with a pty. - ptmx, err := pty.Start(c) - if err != nil { - return err - } - // Make sure to close the pty at the end. - defer func() { _ = ptmx.Close() }() // Best effort. - - // Handle pty size. - ch := make(chan os.Signal, 1) - signal.Notify(ch, syscall.SIGWINCH) - go func() { - for range ch { - if err := pty.InheritSize(os.Stdin, ptmx); err != nil { - log.Printf("error resizing pty: %s", err) - } - } - }() - ch <- syscall.SIGWINCH // Initial resize. - - // Set stdin in raw mode. - oldState, err := terminal.MakeRaw(int(os.Stdin.Fd())) - if err != nil { - panic(err) - } - defer func() { _ = terminal.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort. - - // Copy stdin to the pty and the pty to stdout. - go func() { _, _ = io.Copy(ptmx, os.Stdin) }() - _, _ = io.Copy(os.Stdout, ptmx) - - return nil -} - -func main() { - if err := test(); err != nil { - log.Fatal(err) - } -} -``` diff --git a/vendor/github.com/kr/pty/mktypes.bash b/vendor/github.com/kr/pty/mktypes.bash deleted file mode 100755 index 82ee16721..000000000 --- a/vendor/github.com/kr/pty/mktypes.bash +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -GOOSARCH="${GOOS}_${GOARCH}" -case "$GOOSARCH" in -_* | *_ | _) - echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2 - exit 1 - ;; -esac - -GODEFS="go tool cgo -godefs" - -$GODEFS types.go |gofmt > ztypes_$GOARCH.go - -case $GOOS in -freebsd|dragonfly|openbsd) - $GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go - ;; -esac diff --git a/vendor/github.com/mgutz/str/CREDITS b/vendor/github.com/mgutz/str/CREDITS deleted file mode 100644 index ddb244c30..000000000 --- a/vendor/github.com/mgutz/str/CREDITS +++ /dev/null @@ -1,5 +0,0 @@ -* [string.js](http://stringjs.com) - I contributed several - functions to this project. - -* [bbgen.net](http://bbgen.net/blog/2011/06/string-to-argc-argv/) - diff --git a/vendor/github.com/mgutz/str/README.md b/vendor/github.com/mgutz/str/README.md deleted file mode 100644 index bce814bc2..000000000 --- a/vendor/github.com/mgutz/str/README.md +++ /dev/null @@ -1,649 +0,0 @@ -# str - - import "github.com/mgutz/str" - -Package str is a comprehensive set of string functions to build more Go -awesomeness. Str complements Go's standard packages and does not duplicate -functionality found in `strings` or `strconv`. - -Str is based on plain functions instead of object-based methods, consistent with -Go standard string packages. - - str.Between("foo", "", "") == "foo" - -Str supports pipelining instead of chaining - - s := str.Pipe("\nabcdef\n", Clean, BetweenF("a", "f"), ChompLeftF("bc")) - -User-defined filters can be added to the pipeline by inserting a function or -closure that returns a function with this signature - - func(string) string - -### Index - -* [Variables](#variables) -* [func Between](#func -[godoc](https://godoc.org/github.com/mgutz/str) -between) -* [func BetweenF](#func--betweenf) -* [func Camelize](#func--camelize) -* [func Capitalize](#func--capitalize) -* [func CharAt](#func--charat) -* [func CharAtF](#func--charatf) -* [func ChompLeft](#func--chompleft) -* [func ChompLeftF](#func--chompleftf) -* [func ChompRight](#func--chompright) -* [func ChompRightF](#func--chomprightf) -* [func Classify](#func--classify) -* [func ClassifyF](#func--classifyf) -* [func Clean](#func--clean) -* [func Dasherize](#func--dasherize) -* [func DecodeHTMLEntities](#func--decodehtmlentities) -* [func EnsurePrefix](#func--ensureprefix) -* [func EnsurePrefixF](#func--ensureprefixf) -* [func EnsureSuffix](#func--ensuresuffix) -* [func EnsureSuffixF](#func--ensuresuffixf) -* [func EscapeHTML](#func--escapehtml) -* [func Humanize](#func--humanize) -* [func Iif](#func--iif) -* [func IndexOf](#func--indexof) -* [func IsAlpha](#func--isalpha) -* [func IsAlphaNumeric](#func--isalphanumeric) -* [func IsEmpty](#func--isempty) -* [func IsLower](#func--islower) -* [func IsNumeric](#func--isnumeric) -* [func IsUpper](#func--isupper) -* [func Left](#func--left) -* [func LeftF](#func--leftf) -* [func LeftOf](#func--leftof) -* [func Letters](#func--letters) -* [func Lines](#func--lines) -* [func Map](#func--map) -* [func Match](#func--match) -* [func Pad](#func--pad) -* [func PadF](#func--padf) -* [func PadLeft](#func--padleft) -* [func PadLeftF](#func--padleftf) -* [func PadRight](#func--padright) -* [func PadRightF](#func--padrightf) -* [func Pipe](#func--pipe) -* [func QuoteItems](#func--quoteitems) -* [func ReplaceF](#func--replacef) -* [func ReplacePattern](#func--replacepattern) -* [func ReplacePatternF](#func--replacepatternf) -* [func Reverse](#func--reverse) -* [func Right](#func--right) -* [func RightF](#func--rightf) -* [func RightOf](#func--rightof) -* [func SetTemplateDelimiters](#func--settemplatedelimiters) -* [func Slice](#func--slice) -* [func SliceContains](#func--slicecontains) -* [func SliceF](#func--slicef) -* [func SliceIndexOf](#func--sliceindexof) -* [func Slugify](#func--slugify) -* [func StripPunctuation](#func--strippunctuation) -* [func StripTags](#func--striptags) -* [func Substr](#func--substr) -* [func SubstrF](#func--substrf) -* [func Template](#func--template) -* [func TemplateDelimiters](#func--templatedelimiters) -* [func TemplateWithDelimiters](#func--templatewithdelimiters) -* [func ToArgv](#func--toargv) -* [func ToBool](#func--tobool) -* [func ToBoolOr](#func--toboolor) -* [func ToFloat32Or](#func--tofloat32or) -* [func ToFloat64Or](#func--tofloat64or) -* [func ToIntOr](#func--tointor) -* [func Underscore](#func--underscore) -* [func UnescapeHTML](#func--unescapehtml) -* [func WrapHTML](#func--wraphtml) -* [func WrapHTMLF](#func--wraphtmlf) - - -#### Variables - -```go -var ToFloatOr = ToFloat64Or -``` -ToFloatOr parses as a float64 or returns defaultValue. - -```go -var Verbose = false -``` -Verbose flag enables console output for those functions that have counterparts -in Go's excellent stadard packages. - -#### func [Between](#between) - -```go -func Between(s, left, right string) string -``` -Between extracts a string between left and right strings. - -#### func [BetweenF](#betweenf) - -```go -func BetweenF(left, right string) func(string) string -``` -BetweenF is the filter form for Between. - -#### func [Camelize](#camelize) - -```go -func Camelize(s string) string -``` -Camelize return new string which removes any underscores or dashes and convert a -string into camel casing. - -#### func [Capitalize](#capitalize) - -```go -func Capitalize(s string) string -``` -Capitalize uppercases the first char of s and lowercases the rest. - -#### func [CharAt](#charat) - -```go -func CharAt(s string, index int) string -``` -CharAt returns a string from the character at the specified position. - -#### func [CharAtF](#charatf) - -```go -func CharAtF(index int) func(string) string -``` -CharAtF is the filter form of CharAt. - -#### func [ChompLeft](#chompleft) - -```go -func ChompLeft(s, prefix string) string -``` -ChompLeft removes prefix at the start of a string. - -#### func [ChompLeftF](#chompleftf) - -```go -func ChompLeftF(prefix string) func(string) string -``` -ChompLeftF is the filter form of ChompLeft. - -#### func [ChompRight](#chompright) - -```go -func ChompRight(s, suffix string) string -``` -ChompRight removes suffix from end of s. - -#### func [ChompRightF](#chomprightf) - -```go -func ChompRightF(suffix string) func(string) string -``` -ChompRightF is the filter form of ChompRight. - -#### func [Classify](#classify) - -```go -func Classify(s string) string -``` -Classify returns a camelized string with the first letter upper cased. - -#### func [ClassifyF](#classifyf) - -```go -func ClassifyF(s string) func(string) string -``` -ClassifyF is the filter form of Classify. - -#### func [Clean](#clean) - -```go -func Clean(s string) string -``` -Clean compresses all adjacent whitespace to a single space and trims s. - -#### func [Dasherize](#dasherize) - -```go -func Dasherize(s string) string -``` -Dasherize converts a camel cased string into a string delimited by dashes. - -#### func [DecodeHTMLEntities](#decodehtmlentities) - -```go -func DecodeHTMLEntities(s string) string -``` -DecodeHTMLEntities decodes HTML entities into their proper string -representation. DecodeHTMLEntities is an alias for html.UnescapeString - -#### func [EnsurePrefix](#ensureprefix) - -```go -func EnsurePrefix(s, prefix string) string -``` -EnsurePrefix ensures s starts with prefix. - -#### func [EnsurePrefixF](#ensureprefixf) - -```go -func EnsurePrefixF(prefix string) func(string) string -``` -EnsurePrefixF is the filter form of EnsurePrefix. - -#### func [EnsureSuffix](#ensuresuffix) - -```go -func EnsureSuffix(s, suffix string) string -``` -EnsureSuffix ensures s ends with suffix. - -#### func [EnsureSuffixF](#ensuresuffixf) - -```go -func EnsureSuffixF(suffix string) func(string) string -``` -EnsureSuffixF is the filter form of EnsureSuffix. - -#### func [EscapeHTML](#escapehtml) - -```go -func EscapeHTML(s string) string -``` -EscapeHTML is alias for html.EscapeString. - -#### func [Humanize](#humanize) - -```go -func Humanize(s string) string -``` -Humanize transforms s into a human friendly form. - -#### func [Iif](#iif) - -```go -func Iif(condition bool, truthy string, falsey string) string -``` -Iif is short for immediate if. If condition is true return truthy else falsey. - -#### func [IndexOf](#indexof) - -```go -func IndexOf(s string, needle string, start int) int -``` -IndexOf finds the index of needle in s starting from start. - -#### func [IsAlpha](#isalpha) - -```go -func IsAlpha(s string) bool -``` -IsAlpha returns true if a string contains only letters from ASCII (a-z,A-Z). -Other letters from other languages are not supported. - -#### func [IsAlphaNumeric](#isalphanumeric) - -```go -func IsAlphaNumeric(s string) bool -``` -IsAlphaNumeric returns true if a string contains letters and digits. - -#### func [IsEmpty](#isempty) - -```go -func IsEmpty(s string) bool -``` -IsEmpty returns true if the string is solely composed of whitespace. - -#### func [IsLower](#islower) - -```go -func IsLower(s string) bool -``` -IsLower returns true if s comprised of all lower case characters. - -#### func [IsNumeric](#isnumeric) - -```go -func IsNumeric(s string) bool -``` -IsNumeric returns true if a string contains only digits from 0-9. Other digits -not in Latin (such as Arabic) are not currently supported. - -#### func [IsUpper](#isupper) - -```go -func IsUpper(s string) bool -``` -IsUpper returns true if s contains all upper case chracters. - -#### func [Left](#left) - -```go -func Left(s string, n int) string -``` -Left returns the left substring of length n. - -#### func [LeftF](#leftf) - -```go -func LeftF(n int) func(string) string -``` -LeftF is the filter form of Left. - -#### func [LeftOf](#leftof) - -```go -func LeftOf(s string, needle string) string -``` -LeftOf returns the substring left of needle. - -#### func [Letters](#letters) - -```go -func Letters(s string) []string -``` -Letters returns an array of runes as strings so it can be indexed into. - -#### func [Lines](#lines) - -```go -func Lines(s string) []string -``` -Lines convert windows newlines to unix newlines then convert to an Array of -lines. - -#### func [Map](#map) - -```go -func Map(arr []string, iterator func(string) string) []string -``` -Map maps an array's iitem through an iterator. - -#### func [Match](#match) - -```go -func Match(s, pattern string) bool -``` -Match returns true if patterns matches the string - -#### func [Pad](#pad) - -```go -func Pad(s, c string, n int) string -``` -Pad pads string s on both sides with c until it has length of n. - -#### func [PadF](#padf) - -```go -func PadF(c string, n int) func(string) string -``` -PadF is the filter form of Pad. - -#### func [PadLeft](#padleft) - -```go -func PadLeft(s, c string, n int) string -``` -PadLeft pads s on left side with c until it has length of n. - -#### func [PadLeftF](#padleftf) - -```go -func PadLeftF(c string, n int) func(string) string -``` -PadLeftF is the filter form of PadLeft. - -#### func [PadRight](#padright) - -```go -func PadRight(s, c string, n int) string -``` -PadRight pads s on right side with c until it has length of n. - -#### func [PadRightF](#padrightf) - -```go -func PadRightF(c string, n int) func(string) string -``` -PadRightF is the filter form of Padright - -#### func [Pipe](#pipe) - -```go -func Pipe(s string, funcs ...func(string) string) string -``` -Pipe pipes s through one or more string filters. - -#### func [QuoteItems](#quoteitems) - -```go -func QuoteItems(arr []string) []string -``` -QuoteItems quotes all items in array, mostly for debugging. - -#### func [ReplaceF](#replacef) - -```go -func ReplaceF(old, new string, n int) func(string) string -``` -ReplaceF is the filter form of strings.Replace. - -#### func [ReplacePattern](#replacepattern) - -```go -func ReplacePattern(s, pattern, repl string) string -``` -ReplacePattern replaces string with regexp string. ReplacePattern returns a copy -of src, replacing matches of the Regexp with the replacement string repl. Inside -repl, $ signs are interpreted as in Expand, so for instance $1 represents the -text of the first submatch. - -#### func [ReplacePatternF](#replacepatternf) - -```go -func ReplacePatternF(pattern, repl string) func(string) string -``` -ReplacePatternF is the filter form of ReplaceRegexp. - -#### func [Reverse](#reverse) - -```go -func Reverse(s string) string -``` -Reverse a string - -#### func [Right](#right) - -```go -func Right(s string, n int) string -``` -Right returns the right substring of length n. - -#### func [RightF](#rightf) - -```go -func RightF(n int) func(string) string -``` -RightF is the Filter version of Right. - -#### func [RightOf](#rightof) - -```go -func RightOf(s string, prefix string) string -``` -RightOf returns the substring to the right of prefix. - -#### func [SetTemplateDelimiters](#settemplatedelimiters) - -```go -func SetTemplateDelimiters(opening, closing string) -``` -SetTemplateDelimiters sets the delimiters for Template function. Defaults to -"{{" and "}}" - -#### func [Slice](#slice) - -```go -func Slice(s string, start, end int) string -``` -Slice slices a string. If end is negative then it is the from the end of the -string. - -#### func [SliceContains](#slicecontains) - -```go -func SliceContains(slice []string, val string) bool -``` -SliceContains determines whether val is an element in slice. - -#### func [SliceF](#slicef) - -```go -func SliceF(start, end int) func(string) string -``` -SliceF is the filter for Slice. - -#### func [SliceIndexOf](#sliceindexof) - -```go -func SliceIndexOf(slice []string, val string) int -``` -SliceIndexOf gets the indx of val in slice. Returns -1 if not found. - -#### func [Slugify](#slugify) - -```go -func Slugify(s string) string -``` -Slugify converts s into a dasherized string suitable for URL segment. - -#### func [StripPunctuation](#strippunctuation) - -```go -func StripPunctuation(s string) string -``` -StripPunctuation strips puncation from string. - -#### func [StripTags](#striptags) - -```go -func StripTags(s string, tags ...string) string -``` -StripTags strips all of the html tags or tags specified by the parameters - -#### func [Substr](#substr) - -```go -func Substr(s string, index int, n int) string -``` -Substr returns a substring of s starting at index of length n. - -#### func [SubstrF](#substrf) - -```go -func SubstrF(index, n int) func(string) string -``` -SubstrF is the filter form of Substr. - -#### func [Template](#template) - -```go -func Template(s string, values map[string]interface{}) string -``` -Template is a string template which replaces template placeholders delimited by -"{{" and "}}" with values from map. The global delimiters may be set with -SetTemplateDelimiters. - -#### func [TemplateDelimiters](#templatedelimiters) - -```go -func TemplateDelimiters() (opening string, closing string) -``` -TemplateDelimiters is the getter for the opening and closing delimiters for -Template. - -#### func [TemplateWithDelimiters](#templatewithdelimiters) - -```go -func TemplateWithDelimiters(s string, values map[string]interface{}, opening, closing string) string -``` -TemplateWithDelimiters is string template with user-defineable opening and -closing delimiters. - -#### func [ToArgv](#toargv) - -```go -func ToArgv(s string) []string -``` -ToArgv converts string s into an argv for exec. - -#### func [ToBool](#tobool) - -```go -func ToBool(s string) bool -``` -ToBool fuzzily converts truthy values. - -#### func [ToBoolOr](#toboolor) - -```go -func ToBoolOr(s string, defaultValue bool) bool -``` -ToBoolOr parses s as a bool or returns defaultValue. - -#### func [ToFloat32Or](#tofloat32or) - -```go -func ToFloat32Or(s string, defaultValue float32) float32 -``` -ToFloat32Or parses as a float32 or returns defaultValue on error. - -#### func [ToFloat64Or](#tofloat64or) - -```go -func ToFloat64Or(s string, defaultValue float64) float64 -``` -ToFloat64Or parses s as a float64 or returns defaultValue. - -#### func [ToIntOr](#tointor) - -```go -func ToIntOr(s string, defaultValue int) int -``` -ToIntOr parses s as an int or returns defaultValue. - -#### func [Underscore](#underscore) - -```go -func Underscore(s string) string -``` -Underscore returns converted camel cased string into a string delimited by -underscores. - -#### func [UnescapeHTML](#unescapehtml) - -```go -func UnescapeHTML(s string) string -``` -UnescapeHTML is an alias for html.UnescapeString. - -#### func [WrapHTML](#wraphtml) - -```go -func WrapHTML(s string, tag string, attrs map[string]string) string -``` -WrapHTML wraps s within HTML tag having attributes attrs. Note, WrapHTML does -not escape s value. - -#### func [WrapHTMLF](#wraphtmlf) - -```go -func WrapHTMLF(tag string, attrs map[string]string) func(string) string -``` -WrapHTMLF is the filter form of WrapHTML. diff --git a/vendor/github.com/mgutz/str/VERSION b/vendor/github.com/mgutz/str/VERSION deleted file mode 100644 index 9084fa2f7..000000000 --- a/vendor/github.com/mgutz/str/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.1.0 diff --git a/vendor/github.com/mgutz/str/str_test.go b/vendor/github.com/mgutz/str/str_test.go deleted file mode 100644 index 3186a387d..000000000 --- a/vendor/github.com/mgutz/str/str_test.go +++ /dev/null @@ -1,696 +0,0 @@ -package str - -//import "testing" -import "fmt" - -//import "strings" - -func ExampleBetween() { - eg(1, Between("foo", "", "")) - eg(2, Between("foo", "", "")) - eg(3, Between("foo", "", "")) - eg(4, Between("foo", "", "")) - eg(5, Between("foo", "", "")) - eg(6, Between("Some strings } are very {weird}, dont you think?", "{", "}")) - eg(7, Between("This is ateststring", "", "test")) - eg(8, Between("This is ateststring", "test", "")) - // Output: - // 1: foo - // 2: foo - // 3: foo - // 4: foo - // 5: - // 6: weird - // 7: This is a - // 8: string -} - -func ExampleBetweenF() { - eg(1, Pipe("abc", BetweenF("a", "c"))) - // Output: - // 1: b -} - -func ExampleCamelize() { - eg(1, Camelize("data_rate")) - eg(2, Camelize("background-color")) - eg(3, Camelize("-moz-something")) - eg(4, Camelize("_car_speed_")) - eg(5, Camelize("yes_we_can")) - // Output: - // 1: dataRate - // 2: backgroundColor - // 3: MozSomething - // 4: CarSpeed - // 5: yesWeCan -} - -func ExampleCapitalize() { - eg(1, Capitalize("abc")) - eg(2, Capitalize("ABC")) - // Output: - // 1: Abc - // 2: Abc -} - -func ExampleCharAt() { - eg(1, CharAt("abc", 1)) - eg(2, CharAt("", -1)) - eg(3, CharAt("", 0)) - eg(4, CharAt("", 10)) - eg(5, CharAt("abc", -1)) - eg(6, CharAt("abc", 10)) - // Output: - // 1: b - // 2: - // 3: - // 4: - // 5: - // 6: -} - -func ExampleCharAtF() { - eg(1, Pipe("abc", CharAtF(1))) - // Output: - // 1: b -} - -func ExampleChompLeft() { - eg(1, ChompLeft("foobar", "foo")) - eg(2, ChompLeft("foobar", "bar")) - eg(3, ChompLeft("", "foo")) - eg(4, ChompLeft("", "")) - eg(5, ChompLeft("foo", "")) - // Output: - // 1: bar - // 2: foobar - // 3: - // 4: - // 5: foo -} - -func ExampleChompLeftF() { - eg(1, Pipe("abc", ChompLeftF("ab"))) - // Output: - // 1: c -} - -func ExampleChompRight() { - eg(1, ChompRight("foobar", "foo")) - eg(2, ChompRight("foobar", "bar")) - eg(3, ChompRight("", "foo")) - eg(4, ChompRight("", "")) - // Output: - // 1: foobar - // 2: foo - // 3: - // 4: -} - -func ExampleChompRightF() { - eg(1, Pipe("abc", ChompRightF("bc"))) - // Output: - // 1: a -} - -func ExampleClassify() { - eg(1, Classify("data_rate")) - eg(2, Classify("background-color")) - eg(3, Classify("-moz-something")) - eg(4, Classify("_car_speed_")) - eg(5, Classify("yes_we_can")) - // Output: - // 1: DataRate - // 2: BackgroundColor - // 3: MozSomething - // 4: CarSpeed - // 5: YesWeCan -} - -func ExampleClean() { - eg(1, Clean("clean")) - eg(2, Clean("")) - eg(3, Clean(" please\t clean \t \n me ")) - // Output: - // 1: clean - // 2: - // 3: please clean me -} - -func ExampleDasherize() { - eg(1, Dasherize("dataRate")) - eg(2, Dasherize("CarSpeed")) - eg(3, Dasherize("yesWeCan")) - eg(4, Dasherize("")) - eg(5, Dasherize("ABC")) - // Output: - // 1: data-rate - // 2: -car-speed - // 3: yes-we-can - // 4: - // 5: -a-b-c -} - -func ExampleDecodeHTMLEntities() { - eg(1, DecodeHTMLEntities("Ken Thompson & Dennis Ritchie")) - eg(2, DecodeHTMLEntities("3 < 4")) - eg(3, DecodeHTMLEntities("http://")) - // Output: - // 1: Ken Thompson & Dennis Ritchie - // 2: 3 < 4 - // 3: http:// -} - -func ExampleEnsurePrefix() { - eg(1, EnsurePrefix("foobar", "foo")) - eg(2, EnsurePrefix("bar", "foo")) - eg(3, EnsurePrefix("", "")) - eg(4, EnsurePrefix("foo", "")) - eg(5, EnsurePrefix("", "foo")) - // Output: - // 1: foobar - // 2: foobar - // 3: - // 4: foo - // 5: foo -} - -func ExampleEnsurePrefixF() { - eg(1, Pipe("dir", EnsurePrefixF("./"))) - // Output: - // 1: ./dir -} - -func ExampleEnsureSuffix() { - eg(1, EnsureSuffix("foobar", "bar")) - eg(2, EnsureSuffix("foo", "bar")) - eg(3, EnsureSuffix("", "")) - eg(4, EnsureSuffix("foo", "")) - eg(5, EnsureSuffix("", "bar")) - // Output: - // 1: foobar - // 2: foobar - // 3: - // 4: foo - // 5: bar -} - -func ExampleHumanize() { - eg(1, Humanize("the_humanize_string_method")) - eg(2, Humanize("ThehumanizeStringMethod")) - eg(3, Humanize("the humanize string method")) - // Output: - // 1: The humanize string method - // 2: Thehumanize string method - // 3: The humanize string method -} - -func ExampleIif() { - eg(1, Iif(true, "T", "F")) - eg(2, Iif(false, "T", "F")) - // Output: - // 1: T - // 2: F -} - -func ExampleIndexOf() { - eg(1, IndexOf("abcdef", "a", 0)) - eg(2, IndexOf("abcdef", "a", 3)) - eg(3, IndexOf("abcdef", "a", -2)) - eg(4, IndexOf("abcdef", "a", 10)) - eg(5, IndexOf("", "a", 0)) - eg(6, IndexOf("abcdef", "", 2)) - eg(7, IndexOf("abcdef", "", 1000)) - // Output: - // 1: 0 - // 2: -1 - // 3: -1 - // 4: -1 - // 5: -1 - // 6: 2 - // 7: 6 -} - -func ExampleIsAlpha() { - eg(1, IsAlpha("afaf")) - eg(2, IsAlpha("FJslfjkasfs")) - eg(3, IsAlpha("áéúóúÁÉÍÓÚãõÃÕàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜçÇ")) - eg(4, IsAlpha("adflj43faljsdf")) - eg(5, IsAlpha("33")) - eg(6, IsAlpha("TT....TTTafafetstYY")) - eg(7, IsAlpha("-áéúóúÁÉÍÓÚãõÃÕàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜçÇ")) - // Output: - // 1: true - // 2: true - // 3: true - // 4: false - // 5: false - // 6: false - // 7: false -} - -func eg(index int, example interface{}) { - output := fmt.Sprintf("%d: %v", index, example) - fmt.Printf("%s\n", Clean(output)) -} - -func ExampleIsAlphaNumeric() { - eg(1, IsAlphaNumeric("afaf35353afaf")) - eg(2, IsAlphaNumeric("FFFF99fff")) - eg(3, IsAlphaNumeric("99")) - eg(4, IsAlphaNumeric("afff")) - eg(5, IsAlphaNumeric("Infinity")) - eg(6, IsAlphaNumeric("áéúóúÁÉÍÓÚãõÃÕàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜçÇ1234567890")) - eg(7, IsAlphaNumeric("-Infinity")) - eg(8, IsAlphaNumeric("-33")) - eg(9, IsAlphaNumeric("aaff..")) - eg(10, IsAlphaNumeric(".áéúóúÁÉÍÓÚãõÃÕàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛäëïöüÄËÏÖÜçÇ1234567890")) - // Output: - // 1: true - // 2: true - // 3: true - // 4: true - // 5: true - // 6: true - // 7: false - // 8: false - // 9: false - // 10: false -} - -func ExampleIsEmpty() { - eg(1, IsEmpty(" ")) - eg(2, IsEmpty("\t\t\t ")) - eg(3, IsEmpty("\t\n ")) - eg(4, IsEmpty("hi")) - // Output: - // 1: true - // 2: true - // 3: true - // 4: false -} - -func ExampleIsLower() { - eg(1, IsLower("a")) - eg(2, IsLower("A")) - eg(3, IsLower("abc")) - eg(4, IsLower("aBc")) - eg(5, IsLower("áéúóúãõàèìòùâêîôûäëïöüç")) - eg(6, IsLower("hi jp")) - eg(7, IsLower("ÁÉÍÓÚÃÕÀÈÌÒÙÂÊÎÔÛÄËÏÖÜÇ")) - eg(8, IsLower("áéúóúãõàèìòùâêîôûäëïöüçÁ")) - eg(9, IsLower("áéúóúãõàèìòùâêîôû äëïöüç")) - // Output: - // 1: true - // 2: false - // 3: true - // 4: false - // 5: true - // 6: false - // 7: false - // 8: false - // 9: false -} - -func ExampleIsNumeric() { - eg(1, IsNumeric("3")) - eg(2, IsNumeric("34.22")) - eg(3, IsNumeric("-22.33")) - eg(4, IsNumeric("NaN")) - eg(5, IsNumeric("Infinity")) - eg(6, IsNumeric("-Infinity")) - eg(7, IsNumeric("JP")) - eg(8, IsNumeric("-5")) - eg(9, IsNumeric("00099242424")) - // Output: - // 1: true - // 2: false - // 3: false - // 4: false - // 5: false - // 6: false - // 7: false - // 8: false - // 9: true -} - -func ExampleIsUpper() { - eg(1, IsUpper("a")) - eg(2, IsUpper("A")) - eg(3, IsUpper("ABC")) - eg(4, IsUpper("aBc")) - eg(5, IsUpper("áéúóúãõàèìòùâêîôûäëïöüç")) - eg(6, IsUpper("HI JP")) - eg(7, IsUpper("ÁÉÍÓÚÃÕÀÈÌÒÙÂÊÎÔÛÄËÏÖÜÇ")) - eg(8, IsUpper("áéúóúãõàèìòùâêîôûäëïöüçÁ")) - eg(9, IsUpper("ÁÉÍÓÚÃÕÀÈÌÒÙÂÊÎ ÔÛÄËÏÖÜÇ")) - // Output: - // 1: false - // 2: true - // 3: true - // 4: false - // 5: false - // 6: false - // 7: true - // 8: false - // 9: false -} - -func ExampleLeft() { - eg(1, Left("abcdef", 0)) - eg(2, Left("abcdef", 1)) - eg(3, Left("abcdef", 4)) - eg(4, Left("abcdef", -2)) - // Output: - // 1: - // 2: a - // 3: abcd - // 4: ef -} - -func ExampleLeftOf() { - eg(1, LeftOf("abcdef", "def")) - eg(2, LeftOf("abcdef", "abc")) - eg(3, LeftOf("abcdef", "")) - eg(4, LeftOf("", "abc")) - eg(5, LeftOf("abcdef", "xyz")) - // Output: - // 1: abc - // 2: - // 3: abcdef - // 4: - // 5: -} - -func ExampleLines() { - eg(1, Lines("a\r\nb\nc\r\n")) - eg(2, Lines("a\r\nb\nc\r\nd")) - // Output: - // 1: [a b c ] - // 2: [a b c d] -} - -func ExampleMatch() { - eg(1, Match("foobar", `^fo.*r$`)) - eg(2, Match("foobar", `^fo.*x$`)) - eg(3, Match("", `^fo.*x$`)) - // Output: - // 1: true - // 2: false - // 3: false -} - -func ExamplePad() { - eg(1, Pad("hello", "x", 5)) - eg(2, Pad("hello", "x", 10)) - eg(3, Pad("hello", "x", 11)) - eg(4, Pad("hello", "x", 6)) - eg(5, Pad("hello", "x", 1)) - // Output: - // 1: hello - // 2: xxxhelloxx - // 3: xxxhelloxxx - // 4: xhello - // 5: hello -} - -func ExamplePadLeft() { - eg(1, PadLeft("hello", "x", 5)) - eg(2, PadLeft("hello", "x", 10)) - eg(3, PadLeft("hello", "x", 11)) - eg(4, PadLeft("hello", "x", 6)) - eg(5, PadLeft("hello", "x", 1)) - // Output: - // 1: hello - // 2: xxxxxhello - // 3: xxxxxxhello - // 4: xhello - // 5: hello -} - -func ExamplePadRight() { - eg(1, PadRight("hello", "x", 5)) - eg(2, PadRight("hello", "x", 10)) - eg(3, PadRight("hello", "x", 11)) - eg(4, PadRight("hello", "x", 6)) - eg(5, PadRight("hello", "x", 1)) - // Output: - // 1: hello - // 2: helloxxxxx - // 3: helloxxxxxx - // 4: hellox - // 5: hello -} - -func ExamplePipe() { - eg(1, Pipe("\nabcdef \n", Clean, BetweenF("a", "f"), ChompLeftF("bc"))) - // Output: - // 1: de -} - -func ExampleReplaceF() { - eg(1, Pipe("abcdefab", ReplaceF("ab", "x", -1))) - eg(2, Pipe("abcdefab", ReplaceF("ab", "x", 1))) - eg(3, Pipe("abcdefab", ReplaceF("ab", "x", 0))) - // Output: - // 1: xcdefx - // 2: xcdefab - // 3: abcdefab -} - -func ExampleReplacePattern() { - eg(1, ReplacePattern("aabbcc", `a`, "x")) - // Output: - // 1: xxbbcc -} - -func ExampleReplacePatternF() { - eg(1, Pipe("aabbcc", ReplacePatternF(`a`, "x"))) - // Output: - // 1: xxbbcc -} - -func ExampleReverse() { - eg(1, Reverse("abc")) - eg(2, Reverse("中文")) - // Output: - // 1: cba - // 2: 文中 -} - -func ExampleRight() { - eg(1, Right("abcdef", 0)) - eg(2, Right("abcdef", 1)) - eg(3, Right("abcdef", 4)) - eg(4, Right("abcdef", -2)) - // Output: - // 1: - // 2: f - // 3: cdef - // 4: ab -} - -func ExampleRightOf() { - eg(1, RightOf("abcdef", "abc")) - eg(2, RightOf("abcdef", "def")) - eg(3, RightOf("abcdef", "")) - eg(4, RightOf("", "abc")) - eg(5, RightOf("abcdef", "xyz")) - // Output: - // 1: def - // 2: - // 3: abcdef - // 4: - // 5: -} - -func ExampleRightF() { - eg(1, Pipe("abcdef", RightF(3))) - // Output: - // 1: def -} - -func ExampleSliceContains() { - eg(1, SliceContains([]string{"foo", "bar"}, "foo")) - eg(2, SliceContains(nil, "foo")) - eg(3, SliceContains([]string{"foo", "bar"}, "bah")) - eg(4, SliceContains([]string{"foo", "bar"}, "")) - // Output: - // 1: true - // 2: false - // 3: false - // 4: false -} - -func ExampleSliceIndexOf() { - eg(1, SliceIndexOf([]string{"foo", "bar"}, "foo")) - eg(2, SliceIndexOf(nil, "foo")) - eg(3, SliceIndexOf([]string{"foo", "bar"}, "bah")) - eg(4, SliceIndexOf([]string{"foo", "bar"}, "")) - eg(5, SliceIndexOf([]string{"foo", "bar"}, "bar")) - // Output: - // 1: 0 - // 2: -1 - // 3: -1 - // 4: -1 - // 5: 1 -} - -func ExampleSlugify() { - eg(1, Slugify("foo bar")) - eg(2, Slugify("foo/bar bah")) - eg(3, Slugify("foo-bar--bah")) - // Output: - // 1: foo-bar - // 2: foobar-bah - // 3: foo-bar-bah -} - -func ExampleStripPunctuation() { - eg(1, StripPunctuation("My, st[ring] *full* of %punct)")) - // Output: - // 1: My string full of punct -} - -func ExampleStripTags() { - eg(1, StripTags("

just some text

")) - eg(2, StripTags("

just some text

", "p")) - eg(3, StripTags("

just some text

", "a", "p")) - eg(4, StripTags("

just some text

", "b")) - // Output: - // 1: just some text - // 2: just some text - // 3: just some text - // 4:

just some text

-} - -func ExampleSubstr() { - eg(1, Substr("abcdef", 2, -1)) - eg(2, Substr("abcdef", 2, 0)) - eg(3, Substr("abcdef", 2, 1)) - eg(4, Substr("abcdef", 2, 3)) - eg(5, Substr("abcdef", 2, 4)) - eg(6, Substr("abcdef", 2, 100)) - eg(7, Substr("abcdef", 0, 1)) - // Output: - // 1: - // 2: - // 3: c - // 4: cde - // 5: cdef - // 6: cdef - // 7: a -} - -func ExampleTemplateWithDelimiters() { - eg(1, TemplateWithDelimiters("Hello {{name}} at {{date-year}}", map[string]interface{}{"name": "foo", "date-year": 2014}, "{{", "}}")) - eg(2, TemplateWithDelimiters("Hello #{name} at #{date-year}", map[string]interface{}{"name": "foo", "date-year": 2014}, "#{", "}")) - eg(3, TemplateWithDelimiters("Hello (name) at (date-year)", map[string]interface{}{"name": "foo", "date-year": 2014}, "(", ")")) - eg(4, TemplateWithDelimiters("Hello [name] at [date-year]", map[string]interface{}{"name": "foo", "date-year": 2014}, "[", "]")) - eg(5, TemplateWithDelimiters("Hello *name* at *date-year*", map[string]interface{}{"name": "foo", "date-year": 2014}, "*", "*")) - eg(6, TemplateWithDelimiters("Hello $name$ at $date-year$", map[string]interface{}{"name": "foo", "date-year": 2014}, "$", "$")) - // Output: - // 1: Hello foo at 2014 - // 2: Hello foo at 2014 - // 3: Hello foo at 2014 - // 4: Hello foo at 2014 - // 5: Hello foo at 2014 - // 6: Hello foo at 2014 -} - -func ExampleTemplate() { - eg(1, Template("Hello {{name}} at {{date-year}}", map[string]interface{}{"name": "foo", "date-year": 2014})) - eg(2, Template("Hello {{name}}", map[string]interface{}{"name": ""})) - SetTemplateDelimiters("{", "}") - eg(3, Template("Hello {name} at {date-year}", map[string]interface{}{"name": "foo", "date-year": 2014})) - // Output: - // 1: Hello foo at 2014 - // 2: Hello - // 3: Hello foo at 2014 -} - -func ExampleToArgv() { - eg(1, QuoteItems(ToArgv(`GO_ENV=test gosu --watch foo@release "some quoted string 'inside'"`))) - eg(2, QuoteItems(ToArgv(`gosu foo\ bar`))) - eg(3, QuoteItems(ToArgv(`gosu --test="some arg" -w -s a=123`))) - // Output: - // 1: ["GO_ENV=test" "gosu" "--watch" "foo@release" "some quoted string 'inside'"] - // 2: ["gosu" "foo bar"] - // 3: ["gosu" "--test=some arg" "-w" "-s" "a=123"] -} - -func ExampleToBool() { - eg(1, ToBool("true")) - eg(2, ToBool("yes")) - eg(3, ToBool("1")) - eg(4, ToBool("on")) - eg(5, ToBool("false")) - eg(6, ToBool("no")) - eg(7, ToBool("0")) - eg(8, ToBool("off")) - eg(9, ToBool("")) - eg(10, ToBool("?")) - // Output: - // 1: true - // 2: true - // 3: true - // 4: true - // 5: false - // 6: false - // 7: false - // 8: false - // 9: false - // 10: false -} - -func ExampleToBoolOr() { - eg(1, ToBoolOr("foo", true)) - eg(2, ToBoolOr("foo", false)) - eg(3, ToBoolOr("true", false)) - eg(4, ToBoolOr("", true)) - // Output: - // 1: true - // 2: false - // 3: true - // 4: true -} - -func ExampleToIntOr() { - eg(1, ToIntOr("foo", 0)) - eg(2, ToIntOr("", 1)) - eg(3, ToIntOr("100", 0)) - eg(4, ToIntOr("-1", 1)) - // Output: - // 1: 0 - // 2: 1 - // 3: 100 - // 4: -1 -} - -func ExampleUnderscore() { - eg(1, Underscore("fooBar")) - eg(2, Underscore("FooBar")) - eg(3, Underscore("")) - eg(4, Underscore("x")) - // Output: - // 1: foo_bar - // 2: _foo_bar - // 3: - // 4: x -} - -func ExampleWrapHTML() { - eg(1, WrapHTML("foo", "span", nil)) - eg(2, WrapHTML("foo", "", nil)) - eg(3, WrapHTML("foo", "", map[string]string{"class": "bar"})) - // Output: - // 1: foo - // 2:
foo
- // 3:
foo
-} - -func ExampleWrapHTMLF() { - eg(1, Pipe("foo", WrapHTMLF("div", nil))) - // Output: - // 1:
foo
-} diff --git a/vendor/vendor.json b/vendor/vendor.json deleted file mode 100644 index 78d7f6762..000000000 --- a/vendor/vendor.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "comment": "", - "ignore": "", - "package": [ - { - "checksumSHA1": "GAqpU/McvDmpHUgguKefJTA9yho=", - "path": "github.com/flynn/go-shlex", - "revision": "3f9db97f856818214da2e1057f8ad84803971cff", - "revisionTime": "2015-05-15T14:53:56Z" - }, - { - "checksumSHA1": "KU+GT3javo9S9oVEJfqQUKPPUwo=", - "path": "github.com/kr/pty", - "revision": "fa756f09eeb418bf1cc6268c66ceaad9bb98f598", - "revisionTime": "2018-06-20T15:12:22Z" - }, - { - "checksumSHA1": "vUGW1FVEUqtyx20qMHuSZS4i4rU=", - "path": "github.com/mgutz/str", - "revision": "968bf66e3da857419e4f6e71b2d5c9ae95682dc4", - "revisionTime": "2015-06-23T15:38:27Z" - } - ], - "rootPath": "github.com/jesseduffield/lazygit" -} From c35255b7a9dd673db5efd28c83f618b6edff6289 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 21:25:49 +1100 Subject: [PATCH 085/118] set stderr ourselves so that we only read the error output if there is any --- pkg/commands/exec_live_default.go | 40 ++++++++----------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 37e27625a..bce912160 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -4,14 +4,14 @@ package commands import ( "bufio" + "bytes" "errors" "os" "os/exec" "strings" - "sync" "unicode/utf8" - "github.com/kr/pty" + "github.com/jesseduffield/pty" "github.com/mgutz/str" ) @@ -21,57 +21,37 @@ import ( // NOTE: If the return data is empty it won't written anything to stdin // NOTE: You don't have to include a enter in the return data this function will do that for you func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { - cmdOutput := []string{} - cmdOutputOffset := 0 - splitCmd := str.ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) cmd.Env = os.Environ() cmd.Env = append(cmd.Env, "LANG=en_US.utf8", "LC_ALL=en_US.UTF-8") - tty, err := pty.Start(cmd) + var stderr bytes.Buffer + cmd.Stderr = &stderr + + ptmx, err := pty.Start(cmd) if err != nil { return err } - lastWritten := "" - - var waitForBufio sync.WaitGroup - waitForBufio.Add(1) - go func() { - scanner := bufio.NewScanner(tty) + scanner := bufio.NewScanner(ptmx) scanner.Split(scanWordsWithNewLines) for scanner.Scan() { toOutput := strings.Trim(scanner.Text(), " ") - cmdOutput = append(cmdOutput, toOutput) toWrite := output(toOutput) if len(toWrite) > 0 { - toWriteSplit := strings.Split(toWrite, " ") - lastWritten = toWriteSplit[len(toWriteSplit)-1] - // don't do len(cmdOutput)-1 because the next value is the username / password - cmdOutputOffset = len(cmdOutput) - _, _ = tty.WriteString(toWrite + "\n") + _, _ = ptmx.WriteString(toWrite + "\n") } } - waitForBufio.Done() }() err = cmd.Wait() - tty.Close() + ptmx.Close() if err != nil { - waitForBufio.Wait() - for i, item := range cmdOutput { - if lastWritten == item && cmdOutputOffset < i { - cmdOutputOffset = i + 1 - } - } - if cmdOutputOffset > len(cmdOutput)-1 { - cmdOutputOffset = len(cmdOutput) - 1 - } - return errors.New(err.Error() + ", " + strings.Join(cmdOutput[cmdOutputOffset:], " ")) + return errors.New(stderr.String()) } return nil From a4beabf4b940b65c26ce056e9bffc50e2d34b908 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 21:27:39 +1100 Subject: [PATCH 086/118] improved pre-push test script --- test/hooks/pre-push | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/hooks/pre-push b/test/hooks/pre-push index 156b9664c..b7cb2e87b 100644 --- a/test/hooks/pre-push +++ b/test/hooks/pre-push @@ -16,7 +16,10 @@ read username echo -n "Password for 'github': " read password -# echo "failed" -# exit 1 +if [ "$username" = "username" -a "$password" = "password" ]; then + echo "success" + exit 0 +fi -exit 0 \ No newline at end of file +>&2 echo "incorrect username/password" +exit 1 From 276ac3a92ea101a2ba599022cd1caffc66934583 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 21:28:09 +1100 Subject: [PATCH 087/118] decrease frequency of refreshing files because it's causing index lock errors --- pkg/gui/gui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index b0062dba8..3144a2c3d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -534,7 +534,7 @@ func (gui *Gui) Run() error { }) } }() - gui.goEvery(g, time.Second*2, gui.refreshFiles) + gui.goEvery(g, time.Second*10, gui.refreshFiles) gui.goEvery(g, time.Millisecond*50, gui.updateLoader) gui.goEvery(g, time.Millisecond*50, gui.renderAppStatus) From 11c7cbe3ac2adf4f6e717640df7a49e5a916d753 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 22:18:48 +1100 Subject: [PATCH 088/118] bump pty fork --- Gopkg.lock | 6 +++--- Gopkg.toml | 4 ++++ vendor/github.com/jesseduffield/pty/run.go | 8 ++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index f5ef4c76c..7168338ef 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -196,12 +196,12 @@ revision = "03e26ff3f1de2c1bc2205113c3aba661312eee00" [[projects]] - digest = "1:15416fd2b58d5b2dfa0bc99185cdff3a8f49e73e022291891cb8d5d820d9760d" + branch = "master" + digest = "1:a46c2f4863e5284ddb255c28750298e04bc8c0fc896bed6056e947673168b7be" name = "github.com/jesseduffield/pty" packages = ["."] pruneopts = "NUT" - revision = "db8e3cd836b82e82e0a9c8edc6896967dd31374f" - version = "v1.1.3" + revision = "02db52c7e406c7abec44c717a173c7715e4c1b62" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index 1f0b03cee..a645c905f 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -37,6 +37,10 @@ branch = "master" name = "github.com/jesseduffield/gocui" +[[constraint]] + branch = "master" + name = "github.com/jesseduffield/pty" + [[constraint]] name = "gopkg.in/src-d/go-git.v4" revision = "43d17e14b714665ab5bc2ecc220b6740779d733f" diff --git a/vendor/github.com/jesseduffield/pty/run.go b/vendor/github.com/jesseduffield/pty/run.go index 2d7f96119..dda19b760 100644 --- a/vendor/github.com/jesseduffield/pty/run.go +++ b/vendor/github.com/jesseduffield/pty/run.go @@ -33,9 +33,13 @@ func StartWithSize(c *exec.Cmd, sz *Winsize) (pty *os.File, err error) { return nil, err } } - c.Stdout = tty + if c.Stdout == nil { + c.Stdout = tty + } + if c.Stderr == nil { + c.Stderr = tty + } c.Stdin = tty - c.Stderr = tty if c.SysProcAttr == nil { c.SysProcAttr = &syscall.SysProcAttr{} } From 865c7c2332d71704b8a6bc22472313daf4997f16 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 22:19:32 +1100 Subject: [PATCH 089/118] minor refactor of credentials panel into its own file --- pkg/gui/commit_message_panel.go | 84 ------------------------- pkg/gui/credentials_panel.go | 108 ++++++++++++++++++++++++++++++++ pkg/gui/keybindings.go | 4 +- pkg/gui/view_helpers.go | 18 ------ 4 files changed, 110 insertions(+), 104 deletions(-) create mode 100644 pkg/gui/credentials_panel.go diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 71f18e1f0..33eb5b218 100644 --- a/pkg/gui/commit_message_panel.go +++ b/pkg/gui/commit_message_panel.go @@ -51,90 +51,6 @@ func (gui *Gui) handleCommitFocused(g *gocui.Gui, v *gocui.View) error { return gui.renderString(g, "options", message) } -type credentials chan string - -// waitForPassUname wait for a username or password input from the credentials popup -func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUname string) string { - gui.credentials = make(chan string) - credentialsView, _ := g.View("credentials") - if passOrUname == "username" { - credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername") - credentialsView.Mask = 0 - } else { - credentialsView.Title = gui.Tr.SLocalize("CredentialsPassword") - credentialsView.Mask = '*' - } - g.Update(func(g *gocui.Gui) error { - err := gui.switchFocus(g, currentView, credentialsView) - if err != nil { - return err - } - gui.RenderCommitLength() - return nil - }) - - // wait for username/passwords input - userInput := <-gui.credentials - return userInput -} - -func (gui *Gui) handlePushConfirm(g *gocui.Gui, v *gocui.View) error { - message := gui.trimmedContent(v) - if message == "" { - // make sure to input something - // if not dune the push progress will run forever - message = "-" - } - gui.credentials <- message - err := gui.refreshFiles(g) - if err != nil { - return err - } - v.Clear() - err = v.SetCursor(0, 0) - if err != nil { - return err - } - _, err = g.SetViewOnBottom("credentials") - if err != nil { - return err - } - nextView, err := gui.g.View("confirmation") - if err != nil { - nextView = gui.getFilesView(g) - } - err = gui.switchFocus(g, nil, nextView) - if err != nil { - return err - } - return gui.refreshCommits(g) -} - -func (gui *Gui) handlePushClose(g *gocui.Gui, v *gocui.View) error { - _, err := g.SetViewOnBottom("credentials") - if err != nil { - return err - } - - gui.credentials <- "-" - return gui.switchFocus(g, nil, gui.getFilesView(g)) -} - -func (gui *Gui) handleCredentialsViewFocused(g *gocui.Gui, v *gocui.View) error { - if _, err := g.SetViewOnTop("credentials"); err != nil { - return err - } - - message := gui.Tr.TemplateLocalize( - "CloseConfirm", - Teml{ - "keyBindClose": "esc", - "keyBindConfirm": "enter", - }, - ) - return gui.renderString(g, "options", message) -} - func (gui *Gui) simpleEditor(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) { switch { case key == gocui.KeyBackspace || key == gocui.KeyBackspace2: diff --git a/pkg/gui/credentials_panel.go b/pkg/gui/credentials_panel.go new file mode 100644 index 000000000..6c8f8b4ef --- /dev/null +++ b/pkg/gui/credentials_panel.go @@ -0,0 +1,108 @@ +package gui + +import ( + "strings" + + "github.com/jesseduffield/gocui" +) + +type credentials chan string + +// waitForPassUname wait for a username or password input from the credentials popup +func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUname string) string { + gui.credentials = make(chan string) + g.Update(func(g *gocui.Gui) error { + credentialsView, _ := g.View("credentials") + if passOrUname == "username" { + credentialsView.Title = gui.Tr.SLocalize("CredentialsUsername") + credentialsView.Mask = 0 + } else { + credentialsView.Title = gui.Tr.SLocalize("CredentialsPassword") + credentialsView.Mask = '*' + } + err := gui.switchFocus(g, currentView, credentialsView) + if err != nil { + return err + } + gui.RenderCommitLength() + return nil + }) + + // wait for username/passwords input + userInput := <-gui.credentials + return userInput +} + +func (gui *Gui) handleSubmitCredential(g *gocui.Gui, v *gocui.View) error { + message := gui.trimmedContent(v) + if message == "" { + // sending an obviously incorrect password so that the program isn't stuck waiting + message = "-" + } + gui.credentials <- message + err := gui.refreshFiles(g) + if err != nil { + return err + } + v.Clear() + err = v.SetCursor(0, 0) + if err != nil { + return err + } + _, err = g.SetViewOnBottom("credentials") + if err != nil { + return err + } + nextView, err := gui.g.View("confirmation") + if err != nil { + nextView = gui.getFilesView(g) + } + err = gui.switchFocus(g, nil, nextView) + if err != nil { + return err + } + return gui.refreshCommits(g) +} + +func (gui *Gui) handleCloseCredentialsView(g *gocui.Gui, v *gocui.View) error { + _, err := g.SetViewOnBottom("credentials") + if err != nil { + return err + } + + gui.credentials <- "-" + return gui.switchFocus(g, nil, gui.getFilesView(g)) +} + +func (gui *Gui) handleCredentialsViewFocused(g *gocui.Gui, v *gocui.View) error { + if _, err := g.SetViewOnTop("credentials"); err != nil { + return err + } + + message := gui.Tr.TemplateLocalize( + "CloseConfirm", + Teml{ + "keyBindClose": "esc", + "keyBindConfirm": "enter", + }, + ) + return gui.renderString(g, "options", message) +} + +// HandleCredentialsPopup handles the views after executing a command that might ask for credentials +func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr error) { + if popupOpened { + _, _ = gui.g.SetViewOnBottom("credentials") + } + if cmdErr != nil { + errMessage := cmdErr.Error() + if strings.Contains(errMessage, "exit status 128") { + errMessage = gui.Tr.SLocalize("PassUnameWrong") + } + // we are not logging this error because it may contain a password + _ = gui.createSpecificErrorPanel(errMessage, gui.getFilesView(gui.g), false) + } else { + _ = gui.closeConfirmationPrompt(g) + _ = gui.refreshSidePanels(g) + } +} diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index b996e2e95..576fee59a 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -397,12 +397,12 @@ func (gui *Gui) GetKeybindings() []*Binding { ViewName: "credentials", Key: gocui.KeyEnter, Modifier: gocui.ModNone, - Handler: gui.handlePushConfirm, + Handler: gui.handleSubmitCredential, }, { ViewName: "credentials", Key: gocui.KeyEsc, Modifier: gocui.ModNone, - Handler: gui.handlePushClose, + Handler: gui.handleCloseCredentialsView, }, { ViewName: "menu", Key: gocui.KeyEsc, diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 4befe1e0d..75270e388 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -313,24 +313,6 @@ func (gui *Gui) resizeCurrentPopupPanel(g *gocui.Gui) error { return nil } -// HandleCredentialsPopup handles the views after executing a command that might ask for credentials -func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr error) { - if popupOpened { - _, _ = gui.g.SetViewOnBottom("credentials") - } - if cmdErr != nil { - errMessage := cmdErr.Error() - if strings.Contains(errMessage, "exit status 128") { - errMessage = gui.Tr.SLocalize("PassUnameWrong") - } - // we are not logging this error because it may contain a password - _ = gui.createSpecificErrorPanel(errMessage, gui.getFilesView(gui.g), false) - } else { - _ = gui.closeConfirmationPrompt(g) - _ = gui.refreshSidePanels(g) - } -} - func (gui *Gui) resizePopupPanel(g *gocui.Gui, v *gocui.View) error { // If the confirmation panel is already displayed, just resize the width, // otherwise continue From 20a94447d7bad91848ae75b391eaf6feb92e10c4 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 22:23:17 +1100 Subject: [PATCH 090/118] explicitly return newlines to our live command stdin --- pkg/commands/exec_live_default.go | 5 +---- pkg/commands/git.go | 2 +- pkg/commands/git_test.go | 2 +- pkg/gui/credentials_panel.go | 8 ++------ 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index bce912160..a61b5f398 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -41,10 +41,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s scanner.Split(scanWordsWithNewLines) for scanner.Scan() { toOutput := strings.Trim(scanner.Text(), " ") - toWrite := output(toOutput) - if len(toWrite) > 0 { - _, _ = ptmx.WriteString(toWrite + "\n") - } + _, _ = ptmx.WriteString(output(toOutput)) } }() diff --git a/pkg/commands/git.go b/pkg/commands/git.go index e708d5c55..a1f01e422 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -266,7 +266,7 @@ func (c *GitCommand) Fetch(unamePassQuestion func(string) string, canAskForCrede if canAskForCredentials { return unamePassQuestion(question) } - return "-" + return "\n" }) } diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 2413976fc..2302391ec 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1045,7 +1045,7 @@ func TestGitCommandPush(t *testing.T) { gitCmd := newDummyGitCommand() gitCmd.OSCommand.command = s.command err := gitCmd.Push("test", s.forcePush, func(passOrUname string) string { - return "-" + return "\n" }) s.test(err) }) diff --git a/pkg/gui/credentials_panel.go b/pkg/gui/credentials_panel.go index 6c8f8b4ef..dd5461441 100644 --- a/pkg/gui/credentials_panel.go +++ b/pkg/gui/credentials_panel.go @@ -30,15 +30,11 @@ func (gui *Gui) waitForPassUname(g *gocui.Gui, currentView *gocui.View, passOrUn // wait for username/passwords input userInput := <-gui.credentials - return userInput + return userInput + "\n" } func (gui *Gui) handleSubmitCredential(g *gocui.Gui, v *gocui.View) error { message := gui.trimmedContent(v) - if message == "" { - // sending an obviously incorrect password so that the program isn't stuck waiting - message = "-" - } gui.credentials <- message err := gui.refreshFiles(g) if err != nil { @@ -70,7 +66,7 @@ func (gui *Gui) handleCloseCredentialsView(g *gocui.Gui, v *gocui.View) error { return err } - gui.credentials <- "-" + gui.credentials <- "" return gui.switchFocus(g, nil, gui.getFilesView(g)) } From 328b57e2cfe5479a1e128f2fe1944899978a4755 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 22:27:50 +1100 Subject: [PATCH 091/118] no longer checking for 'exit status 128' because we're directly returning stderr --- pkg/gui/credentials_panel.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pkg/gui/credentials_panel.go b/pkg/gui/credentials_panel.go index dd5461441..f310ac6ea 100644 --- a/pkg/gui/credentials_panel.go +++ b/pkg/gui/credentials_panel.go @@ -1,8 +1,6 @@ package gui import ( - "strings" - "github.com/jesseduffield/gocui" ) @@ -91,12 +89,8 @@ func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr er _, _ = gui.g.SetViewOnBottom("credentials") } if cmdErr != nil { - errMessage := cmdErr.Error() - if strings.Contains(errMessage, "exit status 128") { - errMessage = gui.Tr.SLocalize("PassUnameWrong") - } // we are not logging this error because it may contain a password - _ = gui.createSpecificErrorPanel(errMessage, gui.getFilesView(gui.g), false) + _ = gui.createSpecificErrorPanel(cmdErr.Error(), gui.getFilesView(gui.g), false) } else { _ = gui.closeConfirmationPrompt(g) _ = gui.refreshSidePanels(g) From 160af3bb99c1ca9f45f70e7dbeae05f47da2983b Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 22:29:07 +1100 Subject: [PATCH 092/118] fix typo --- pkg/gui/gui.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 3144a2c3d..8ead65be3 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -436,14 +436,14 @@ func (gui *Gui) promptAnonymousReporting() error { }) } -func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canSskForCredentials bool) (unamePassOpend bool, err error) { +func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canAskForCredentials bool) (unamePassOpend bool, err error) { unamePassOpend = false err = gui.GitCommand.Fetch(func(passOrUname string) string { unamePassOpend = true return gui.waitForPassUname(gui.g, v, passOrUname) - }, canSskForCredentials) + }, canAskForCredentials) - if canSskForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") { + if canAskForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") { colorFunction := color.New(color.FgRed).SprintFunc() coloredMessage := colorFunction(strings.TrimSpace(gui.Tr.SLocalize("PassUnameWrong"))) close := func(g *gocui.Gui, v *gocui.View) error { From 535152e15ec84a2bcca0f7685f092903b35f005a Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 22:37:10 +1100 Subject: [PATCH 093/118] bump go.mod --- go.mod | 1 + 1 file changed, 1 insertion(+) diff --git a/go.mod b/go.mod index bfa4f7cf4..5b62d4861 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( 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-20180921065632-03e26ff3f1de + github.com/jesseduffield/pty v0.0.0-20181218102224-02db52c7e406 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 From 7835fce7084632ba93ea5f95c20d4cdfbeb526e6 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 22:40:36 +1100 Subject: [PATCH 094/118] fix tests --- pkg/commands/git_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 2302391ec..3dcb7798d 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1010,7 +1010,7 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Contains(t, err.Error(), "exit status 1") + assert.Contains(t, err.Error(), "error: failed to push some refs") }, }, { @@ -1023,7 +1023,7 @@ func TestGitCommandPush(t *testing.T) { }, true, func(err error) { - assert.Contains(t, err.Error(), "exit status 1") + assert.Contains(t, err.Error(), "error: failed to push some refs") }, }, { @@ -1035,7 +1035,7 @@ func TestGitCommandPush(t *testing.T) { }, false, func(err error) { - assert.Contains(t, err.Error(), "exit status 1") + assert.Contains(t, err.Error(), "error: failed to push some refs") }, }, } From 4793232a358156f1a433d039d0ae54d1a759ddbc Mon Sep 17 00:00:00 2001 From: Cameron Nemo Date: Sun, 16 Dec 2018 14:21:15 -0800 Subject: [PATCH 095/118] Update gomodule checksums for go 1.11.4 --- go.sum | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/go.sum b/go.sum index 1506becb9..ad04aed70 100644 --- a/go.sum +++ b/go.sum @@ -19,7 +19,7 @@ github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1: github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= 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= +github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 h1:qC+3MHkvfCXb1cA9YDpWZ7np8tPOXZceLrW+xyqOgmk= github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001/go.mod h1:6rdJFnhkXnzGOJbvkrdv4t9nLwKcVA+tmbQeUlkIzrU= github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc h1:wAa9fGALVHfjYxZuXRnmuJG2CnwRpJYOTvY6YdErAh0= github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= @@ -31,10 +31,10 @@ github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 h1:qio0y/sQdhbHRA3c github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331/go.mod h1:BT+PgT529opmb6mcUY+Fg0IwVRRmwqFyavEMU17GnBg= 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 h1:tbm85YuPi3d1LFAUr6yZyzZ4vR96ygV98rezZZ+ywbg= github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63/go.mod h1:fNqjRf+4XnTo2PrGN1JRb79b/BeoHwP4lU00f39SQY0= -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/gocui v0.0.0-20180921065632-03e26ff3f1de h1:fOC4rn3guef0ccanBR1+qjHmVIPo01gmZu1aCQndI/s= +github.com/jesseduffield/gocui v0.0.0-20180921065632-03e26ff3f1de/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= @@ -61,8 +61,8 @@ github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun 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/nicksnyder/go-i18n/v2 v2.0.0-beta.6 h1:SooTCzUOOs899x/M7gmSS+dgL+AUfSWqAcHLN3auCic= +github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.6/go.mod h1:4Opqa6/HIv0lhG3WRAkqzO0afezkRhxXI0P8EJkqeRU= 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= From a1b688f0702e364286832346ba3094577cb00941 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 9 Dec 2018 21:51:54 +1100 Subject: [PATCH 096/118] bump gocui fork to convert tabs to spaces --- Gopkg.lock | 4 ++-- vendor/github.com/jesseduffield/gocui/view.go | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 4b84d6537..65df62505 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -189,11 +189,11 @@ [[projects]] branch = "master" - digest = "1:66bb9b4a5abb704642fccba52a84a7f7feef2d9623f87b700e52a6695044723f" + digest = "1:68858ed02f6da4576787f8198290466438b76d287537c7654b56dc10409c0b71" name = "github.com/jesseduffield/gocui" packages = ["."] pruneopts = "NUT" - revision = "03e26ff3f1de2c1bc2205113c3aba661312eee00" + revision = "fe55a32c8a4c7cf94b04a6507eae7ece48c2f975" [[projects]] branch = "master" diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 939d1bdfa..6b80b9050 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -148,7 +148,6 @@ func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error { if x < 0 || x >= maxX || y < 0 || y >= maxY { return errors.New("invalid point") } - var ( ry, rcy int err error @@ -270,12 +269,19 @@ func (v *View) parseInput(ch rune) []cell { if isEscape { return nil } - c := cell{ - fgColor: v.ei.curFgColor, - bgColor: v.ei.curBgColor, - chr: ch, + repeatCount := 1 + if ch == '\t' { + ch = ' ' + repeatCount = 4 + } + for i := 0; i < repeatCount; i++ { + c := cell{ + fgColor: v.ei.curFgColor, + bgColor: v.ei.curBgColor, + chr: ch, + } + cells = append(cells, c) } - cells = append(cells, c) } return cells From c14a4eed0e7aaeeb2afd374a6fbda6b716d83b65 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 22:54:12 +1100 Subject: [PATCH 097/118] bump modules and add bump_modules script --- go.mod | 2 +- scripts/bump_modules.sh | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100755 scripts/bump_modules.sh diff --git a/go.mod b/go.mod index bfa4f7cf4..3ecbc3f6a 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ 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-20180921065632-03e26ff3f1de + github.com/jesseduffield/gocui v0.0.0-20181209104758-fe55a32c8a4c 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 diff --git a/scripts/bump_modules.sh b/scripts/bump_modules.sh new file mode 100755 index 000000000..ffcb0834f --- /dev/null +++ b/scripts/bump_modules.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +GO111MODULE=on +mv go.mod /tmp/ +go mod init \ No newline at end of file From cf56dcf9ff1b548b2becd9058dbcd3d81639bce4 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Dec 2018 23:04:32 +1100 Subject: [PATCH 098/118] refresh files every 10 seconds rather than 2 so that we're not getting a heap of index lock errors --- pkg/gui/gui.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 26ebd5f2b..2af7e77c7 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -472,7 +472,7 @@ func (gui *Gui) Run() error { } gui.goEvery(g, time.Second*60, gui.fetch) - gui.goEvery(g, time.Second*2, gui.refreshFiles) + gui.goEvery(g, time.Second*10, gui.refreshFiles) gui.goEvery(g, time.Millisecond*50, gui.updateLoader) gui.goEvery(g, time.Millisecond*50, gui.renderAppStatus) From 097f687efec8f6c1db1b28631b03520df2419b83 Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 19 Dec 2018 08:56:59 +0100 Subject: [PATCH 099/118] hopefully fixed circleci --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 90f3c9fe7..1ae472ada 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ 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-20181209104758-fe55a32c8a4c + github.com/jesseduffield/gocui v0.0.0-20181209104758-fe55a32c8a4c github.com/jesseduffield/pty v0.0.0-20181218102224-02db52c7e406 github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 From 800b40ecc4ba4c144723214dfbc063d536075f1d Mon Sep 17 00:00:00 2001 From: mjarkk Date: Wed, 19 Dec 2018 10:06:58 +0100 Subject: [PATCH 100/118] Translated credentials error with git push/pull/fetch --- pkg/commands/exec_live_default.go | 2 +- pkg/gui/credentials_panel.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index a61b5f398..6063bb16a 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -25,7 +25,7 @@ func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(s cmd := exec.Command(splitCmd[0], splitCmd[1:]...) cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "LANG=en_US.utf8", "LC_ALL=en_US.UTF-8") + cmd.Env = append(cmd.Env, "LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8") var stderr bytes.Buffer cmd.Stderr = &stderr diff --git a/pkg/gui/credentials_panel.go b/pkg/gui/credentials_panel.go index f310ac6ea..52ec3e405 100644 --- a/pkg/gui/credentials_panel.go +++ b/pkg/gui/credentials_panel.go @@ -1,6 +1,9 @@ package gui import ( + "errors" + "strings" + "github.com/jesseduffield/gocui" ) @@ -89,6 +92,9 @@ func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr er _, _ = gui.g.SetViewOnBottom("credentials") } if cmdErr != nil { + if strings.Contains(cmdErr.Error(), "Invalid username or password") { + cmdErr = errors.New(gui.Tr.SLocalize("PassUnameWrong")) + } // we are not logging this error because it may contain a password _ = gui.createSpecificErrorPanel(cmdErr.Error(), gui.getFilesView(gui.g), false) } else { From 6c5e409ffa5ccc76e7364971e4e0582e45dbab54 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Dec 2018 20:12:35 +1100 Subject: [PATCH 101/118] send direct error message --- pkg/gui/credentials_panel.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/gui/credentials_panel.go b/pkg/gui/credentials_panel.go index 52ec3e405..bc894c422 100644 --- a/pkg/gui/credentials_panel.go +++ b/pkg/gui/credentials_panel.go @@ -1,7 +1,6 @@ package gui import ( - "errors" "strings" "github.com/jesseduffield/gocui" @@ -92,11 +91,12 @@ func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, popupOpened bool, cmdErr er _, _ = gui.g.SetViewOnBottom("credentials") } if cmdErr != nil { - if strings.Contains(cmdErr.Error(), "Invalid username or password") { - cmdErr = errors.New(gui.Tr.SLocalize("PassUnameWrong")) + errMessage := cmdErr.Error() + if strings.Contains(errMessage, "Invalid username or password") { + errMessage = gui.Tr.SLocalize("PassUnameWrong") } // we are not logging this error because it may contain a password - _ = gui.createSpecificErrorPanel(cmdErr.Error(), gui.getFilesView(gui.g), false) + _ = gui.createSpecificErrorPanel(errMessage, gui.getFilesView(gui.g), false) } else { _ = gui.closeConfirmationPrompt(g) _ = gui.refreshSidePanels(g) From 0afffd03cab29d133ab1458964732e29a66ae757 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Dec 2018 21:46:48 +1100 Subject: [PATCH 102/118] remove comment --- pkg/commands/exec_live_default.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/commands/exec_live_default.go b/pkg/commands/exec_live_default.go index 6063bb16a..dfdff5308 100644 --- a/pkg/commands/exec_live_default.go +++ b/pkg/commands/exec_live_default.go @@ -19,7 +19,6 @@ import ( // Output is a function that executes by every word that gets read by bufio // As return of output you need to give a string that will be written to stdin // NOTE: If the return data is empty it won't written anything to stdin -// NOTE: You don't have to include a enter in the return data this function will do that for you func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { splitCmd := str.ToArgv(command) cmd := exec.Command(splitCmd[0], splitCmd[1:]...) From e4f70278ddaa607e4eac2ed3834e1b0c4498abd3 Mon Sep 17 00:00:00 2001 From: Dawid Dziurla Date: Fri, 7 Dec 2018 21:20:14 +0100 Subject: [PATCH 103/118] i18n: pl translation --- pkg/i18n/polish.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/i18n/polish.go b/pkg/i18n/polish.go index d08ffc170..cc8857a30 100644 --- a/pkg/i18n/polish.go +++ b/pkg/i18n/polish.go @@ -409,28 +409,28 @@ func addPolish(i18nObject *i18n.Bundle) error { Other: `Lazygit can't use "git fetch" in a private repo use f in the branches panel to run "git fetch" manually`, }, &i18n.Message{ ID: "StageLines", - Other: `stage individual hunks/lines`, + Other: `zatwierdź pojedyncze linie`, }, &i18n.Message{ ID: "FileStagingRequirements", - Other: `Can only stage individual lines for tracked files with unstaged changes`, + Other: `Można tylko zatwierdzić pojedyncze linie dla śledzonych plików z niezatwierdzonymi zmianami`, }, &i18n.Message{ ID: "StagingTitle", - Other: `Staging`, + Other: `Zatwierdzanie`, }, &i18n.Message{ ID: "StageHunk", - Other: `stage hunk`, + Other: `zatwierdź kawałek`, }, &i18n.Message{ ID: "StageLine", - Other: `stage line`, + Other: `zatwierdź linię`, }, &i18n.Message{ ID: "EscapeStaging", - Other: `return to files panel`, + Other: `wróć do panelu plików`, }, &i18n.Message{ ID: "CantFindHunks", - Other: `Could not find any hunks in this patch`, + Other: `Nie można znaleźć żadnych kawałków w tej łatce`, }, &i18n.Message{ ID: "CantFindHunk", - Other: `Could not find hunk`, + Other: `Nie można znaleźć kawałka`, }, ) } From 47d50989c4f2ee3914dbfe6f7ee04b806ef0406d Mon Sep 17 00:00:00 2001 From: mjarkk Date: Thu, 6 Dec 2018 09:14:32 +0100 Subject: [PATCH 104/118] Added dutch translations --- pkg/i18n/dutch.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index 352f29698..d7ba0278a 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -426,10 +426,10 @@ func addDutch(i18nObject *i18n.Bundle) error { Other: `Lazygit kan niet "git fetch" uitvoeren in een privé repository, gebruik f in het branches paneel om "git fetch" manueel uit te voeren`, }, &i18n.Message{ ID: "StageLines", - Other: `stage individual hunks/lines`, + Other: `stage individuele hunks/lijnen`, }, &i18n.Message{ ID: "FileStagingRequirements", - Other: `Can only stage individual lines for tracked files with unstaged changes`, + Other: `Kan alleen individuele lijnen stagen van getrackte bestanden met onstaged veranderingen`, }, &i18n.Message{ ID: "StagingTitle", Other: `Staging`, @@ -438,16 +438,16 @@ func addDutch(i18nObject *i18n.Bundle) error { Other: `stage hunk`, }, &i18n.Message{ ID: "StageLine", - Other: `stage line`, + Other: `stage lijn`, }, &i18n.Message{ ID: "EscapeStaging", - Other: `return to files panel`, + Other: `ga terug naar het bestanden paneel`, }, &i18n.Message{ ID: "CantFindHunks", - Other: `Could not find any hunks in this patch`, + Other: `Kan geen hunks vinden in deze patch`, }, &i18n.Message{ ID: "CantFindHunk", - Other: `Could not find hunk`, + Other: `kan geen hunk finden`, }, ) } From 666ea3a4a0c12dfe25a787c28c37f07dba279bea Mon Sep 17 00:00:00 2001 From: Mark Kopenga Date: Sat, 8 Dec 2018 15:54:18 +0100 Subject: [PATCH 105/118] Fixed suggestion from glvr182 --- pkg/i18n/dutch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index d7ba0278a..e880d7f0b 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -447,7 +447,7 @@ func addDutch(i18nObject *i18n.Bundle) error { Other: `Kan geen hunks vinden in deze patch`, }, &i18n.Message{ ID: "CantFindHunk", - Other: `kan geen hunk finden`, + Other: `kan geen hunk vinden`, }, ) } From 1db880177189f9654fe7c5486aca5d5881c1f0ee Mon Sep 17 00:00:00 2001 From: Mark Kopenga Date: Tue, 11 Dec 2018 14:44:30 +0100 Subject: [PATCH 106/118] Update dutch.go --- pkg/i18n/dutch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/i18n/dutch.go b/pkg/i18n/dutch.go index e880d7f0b..422c2b283 100644 --- a/pkg/i18n/dutch.go +++ b/pkg/i18n/dutch.go @@ -447,7 +447,7 @@ func addDutch(i18nObject *i18n.Bundle) error { Other: `Kan geen hunks vinden in deze patch`, }, &i18n.Message{ ID: "CantFindHunk", - Other: `kan geen hunk vinden`, + Other: `Kan geen hunk vinden`, }, ) } From 7ff022f1e7a0b8cb19943b040619ec21b4186be0 Mon Sep 17 00:00:00 2001 From: KimMachineGune Date: Thu, 10 Jan 2019 07:42:05 +0900 Subject: [PATCH 107/118] pkg/updates: Refactoring --- pkg/updates/updates.go | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/pkg/updates/updates.go b/pkg/updates/updates.go index 4ec374616..a08a2edcc 100644 --- a/pkg/updates/updates.go +++ b/pkg/updates/updates.go @@ -36,25 +36,24 @@ type Updaterer interface { Update() } -var ( - projectUrl = "https://github.com/jesseduffield/lazygit" +const ( + PROJECT_URL = "https://github.com/jesseduffield/lazygit" ) // NewUpdater creates a new updater func NewUpdater(log *logrus.Entry, config config.AppConfigurer, osCommand *commands.OSCommand, tr *i18n.Localizer) (*Updater, error) { contextLogger := log.WithField("context", "updates") - updater := &Updater{ + return &Updater{ Log: contextLogger, Config: config, OSCommand: osCommand, Tr: tr, - } - return updater, nil + }, nil } func (u *Updater) getLatestVersionNumber() (string, error) { - req, err := http.NewRequest("GET", projectUrl+"/releases/latest", nil) + req, err := http.NewRequest("GET", PROJECT_URL+"/releases/latest", nil) if err != nil { return "", err } @@ -65,17 +64,16 @@ func (u *Updater) getLatestVersionNumber() (string, error) { return "", err } defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { + + dec := json.NewDecoder(resp.Body) + data := struct { + TagName string `json:"tag_name"` + }{} + if err := dec.Decode(&data); err != nil { return "", err } - byt := []byte(body) - var dat map[string]interface{} - if err := json.Unmarshal(byt, &dat); err != nil { - return "", err - } - return dat["tag_name"].(string), nil + return data.TagName, nil } // RecordLastUpdateCheck records last time an update check was performed @@ -225,14 +223,14 @@ func (u *Updater) getBinaryUrl(newVersion string) (string, error) { } url := fmt.Sprintf( "%s/releases/download/%s/lazygit_%s_%s_%s.%s", - projectUrl, + PROJECT_URL, newVersion, newVersion[1:], u.mappedOs(runtime.GOOS), u.mappedArch(runtime.GOARCH), extension, ) - u.Log.Info("url for latest release is " + url) + u.Log.Info("Url for latest release is " + url) return url, nil } @@ -251,7 +249,7 @@ func (u *Updater) update(newVersion string) error { if err != nil { return err } - u.Log.Info("updating with url " + rawUrl) + u.Log.Info("Updating with url " + rawUrl) return u.downloadAndInstall(rawUrl) } @@ -267,7 +265,7 @@ func (u *Updater) downloadAndInstall(rawUrl string) error { return err } defer os.RemoveAll(tempDir) - u.Log.Info("temp directory is " + tempDir) + u.Log.Info("Temp directory is " + tempDir) // Get it! if err := g.Get(tempDir, url); err != nil { @@ -279,14 +277,14 @@ func (u *Updater) downloadAndInstall(rawUrl string) error { if err != nil { return err } - u.Log.Info("binary path is " + binaryPath) + u.Log.Info("Binary path is " + binaryPath) binaryName := filepath.Base(binaryPath) - u.Log.Info("binary name is " + binaryName) + u.Log.Info("Binary name is " + binaryName) // Verify the main file exists tempPath := filepath.Join(tempDir, binaryName) - u.Log.Info("temp path to binary is " + tempPath) + u.Log.Info("Temp path to binary is " + tempPath) if _, err := os.Stat(tempPath); err != nil { return err } @@ -296,7 +294,7 @@ func (u *Updater) downloadAndInstall(rawUrl string) error { if err != nil { return err } - u.Log.Info("update complete!") + u.Log.Info("Update complete!") return nil } From e50bd812fc4cfaf7dca262863e3739771c178744 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 14 Jan 2019 22:04:56 +1100 Subject: [PATCH 108/118] fix checksum mismatches --- go.sum | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/go.sum b/go.sum index ad04aed70..12cbeb220 100644 --- a/go.sum +++ b/go.sum @@ -19,7 +19,7 @@ github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1: github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= 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:qC+3MHkvfCXb1cA9YDpWZ7np8tPOXZceLrW+xyqOgmk= +github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 h1:MFPzqpPED05pFyGjNPJEC2sXM6EHTzFyvX+0s0JoZ48= github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001/go.mod h1:6rdJFnhkXnzGOJbvkrdv4t9nLwKcVA+tmbQeUlkIzrU= github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc h1:wAa9fGALVHfjYxZuXRnmuJG2CnwRpJYOTvY6YdErAh0= github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= @@ -31,10 +31,12 @@ github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 h1:qio0y/sQdhbHRA3c github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331/go.mod h1:BT+PgT529opmb6mcUY+Fg0IwVRRmwqFyavEMU17GnBg= 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:tbm85YuPi3d1LFAUr6yZyzZ4vR96ygV98rezZZ+ywbg= +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-20180921065632-03e26ff3f1de h1:fOC4rn3guef0ccanBR1+qjHmVIPo01gmZu1aCQndI/s= -github.com/jesseduffield/gocui v0.0.0-20180921065632-03e26ff3f1de/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw= +github.com/jesseduffield/gocui v0.0.0-20181209104758-fe55a32c8a4c h1:jEfh/vAtfF3pQ8xFhpYR/0S4iHo11VfaYelJmzZJm94= +github.com/jesseduffield/gocui v0.0.0-20181209104758-fe55a32c8a4c/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw= +github.com/jesseduffield/pty v0.0.0-20181218102224-02db52c7e406 h1:iYMH6h6SuWuBkIzRtymosE8NpSgTK0oRMfyTdVWgxzc= +github.com/jesseduffield/pty v0.0.0-20181218102224-02db52c7e406/go.mod h1:7jlS40+UhOqkZJDIG1B/H21xnuET/+fvbbnHCa8wSIo= 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= From c759c7ac6537eb2ed8f65b566d898a2f2048f9d5 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 15 Jan 2019 19:49:34 +1100 Subject: [PATCH 109/118] bump gocui to prevent truncating the contents of wrapped views --- Gopkg.lock | 4 ++-- go.mod | 2 +- vendor/github.com/jesseduffield/gocui/view.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index ca28d48ca..432138f18 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -189,11 +189,11 @@ [[projects]] branch = "master" - digest = "1:68858ed02f6da4576787f8198290466438b76d287537c7654b56dc10409c0b71" + digest = "1:9b266d7748a5d94985fd9e323494f5b8ae1ab3e910418e898dfe7f03339ddbcd" name = "github.com/jesseduffield/gocui" packages = ["."] pruneopts = "NUT" - revision = "fe55a32c8a4c7cf94b04a6507eae7ece48c2f975" + revision = "cfa9e452ba5ebf014041846851152d64a59dce14" [[projects]] branch = "master" diff --git a/go.mod b/go.mod index 1ae472ada..c01fd368f 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ 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-20181209104758-fe55a32c8a4c + github.com/jesseduffield/gocui v0.0.0-20190115084758-cfa9e452ba5e github.com/jesseduffield/pty v0.0.0-20181218102224-02db52c7e406 github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 6b80b9050..73df1dbcf 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -539,7 +539,7 @@ func lineWrap(line []cell, columns int) [][]cell { n += rw if n > columns { n = rw - lines = append(lines, line[offset:i-1]) + lines = append(lines, line[offset:i]) offset = i } } From c722ea5afcb47a823f76b2e8270f60534f1a9230 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 16 Jan 2019 08:46:05 +1100 Subject: [PATCH 110/118] log in config directory rather than wherever you opened lazygit --- pkg/app/app.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index 74152b08b..ad5edca70 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -4,6 +4,7 @@ import ( "io" "io/ioutil" "os" + "path/filepath" "github.com/heroku/rollrus" "github.com/jesseduffield/lazygit/pkg/commands" @@ -11,6 +12,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui" "github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/updates" + "github.com/shibukawa/configdir" "github.com/sirupsen/logrus" ) @@ -33,9 +35,15 @@ func newProductionLogger(config config.AppConfigurer) *logrus.Logger { return log } -func newDevelopmentLogger() *logrus.Logger { +func globalConfigDir() string { + configDirs := configdir.New("jesseduffield", "lazygit") + configDir := configDirs.QueryFolders(configdir.Global)[0] + return configDir.Path +} + +func newDevelopmentLogger(config config.AppConfigurer) *logrus.Logger { log := logrus.New() - file, err := os.OpenFile("development.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) + file, err := os.OpenFile(filepath.Join(globalConfigDir(), "development.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { panic("unable to log to file") // TODO: don't panic (also, remove this call to the `panic` function) } @@ -48,7 +56,7 @@ func newLogger(config config.AppConfigurer) *logrus.Entry { environment := "production" if config.GetDebug() { environment = "development" - log = newDevelopmentLogger() + log = newDevelopmentLogger(config) } else { log = newProductionLogger(config) } From 3e2406972248ab824b22c7c3965b5f5aab8c9b7b Mon Sep 17 00:00:00 2001 From: KimMachineGune Date: Wed, 16 Jan 2019 14:56:47 +0900 Subject: [PATCH 111/118] pkg: Fix typo --- pkg/commands/exec_live_win.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/commands/exec_live_win.go b/pkg/commands/exec_live_win.go index 343e0dd9f..d06cb920b 100644 --- a/pkg/commands/exec_live_win.go +++ b/pkg/commands/exec_live_win.go @@ -3,7 +3,7 @@ package commands // RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there -// TODO: Remove this hack and replace it with a propper way to run commands live on windows +// TODO: Remove this hack and replace it with a proper way to run commands live on windows func RunCommandWithOutputLiveWrapper(c *OSCommand, command string, output func(string) string) error { return c.RunCommand(command) } From b6f8ebc0ca1c4cdaa63bfd12f5507d368171b872 Mon Sep 17 00:00:00 2001 From: Dawid Dziurla Date: Wed, 16 Jan 2019 18:42:54 +0100 Subject: [PATCH 112/118] delete KeyReadable field from Binding struct also rewrite GetKey function --- pkg/gui/keybindings.go | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 576fee59a..f65852a31 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -12,7 +12,6 @@ type Binding struct { Handler func(*gocui.Gui, *gocui.View) error Key interface{} // FIXME: find out how to get `gocui.Key | rune` Modifier gocui.Modifier - KeyReadable string Description string } @@ -23,16 +22,26 @@ func (b *Binding) GetDisplayStrings() []string { // GetKey is a function. func (b *Binding) GetKey() string { - r, ok := b.Key.(rune) - key := "" + key := 0 - if ok { - key = string(r) - } else if b.KeyReadable != "" { - key = b.KeyReadable + switch b.Key.(type) { + case rune: + key = int(b.Key.(rune)) + case gocui.Key: + key = int(b.Key.(gocui.Key)) } - return key + // special keys + switch key { + case 27: + return "esc" + case 13: + return "enter" + case 32: + return "space" + } + + return string(key) } // GetKeybindings is a function. @@ -144,7 +153,6 @@ func (gui *Gui) GetKeybindings() []*Binding { Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleFilePress, - KeyReadable: "space", Description: gui.Tr.SLocalize("toggleStaged"), }, { ViewName: "files", @@ -218,7 +226,6 @@ func (gui *Gui) GetKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handleSwitchToStagingPanel, Description: gui.Tr.SLocalize("StageLines"), - KeyReadable: "enter", }, { ViewName: "files", Key: 'f', @@ -290,7 +297,6 @@ func (gui *Gui) GetKeybindings() []*Binding { Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleBranchPress, - KeyReadable: "space", Description: gui.Tr.SLocalize("checkout"), }, { ViewName: "branches", @@ -369,7 +375,6 @@ func (gui *Gui) GetKeybindings() []*Binding { Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleStashApply, - KeyReadable: "space", Description: gui.Tr.SLocalize("apply"), }, { ViewName: "stash", @@ -418,7 +423,6 @@ func (gui *Gui) GetKeybindings() []*Binding { Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: gui.handleStagingEscape, - KeyReadable: "esc", Description: gui.Tr.SLocalize("EscapeStaging"), }, { ViewName: "staging", From d5401ab20016ced84638faeb32f2c4c1363e377d Mon Sep 17 00:00:00 2001 From: Dawid Dziurla Date: Wed, 16 Jan 2019 18:47:15 +0100 Subject: [PATCH 113/118] add script generating keybindings cheatsheet --- scripts/generate_cheatsheet.go | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 scripts/generate_cheatsheet.go diff --git a/scripts/generate_cheatsheet.go b/scripts/generate_cheatsheet.go new file mode 100644 index 000000000..466d26e3d --- /dev/null +++ b/scripts/generate_cheatsheet.go @@ -0,0 +1,63 @@ +// This "script" generates a file called Keybindings_{{.LANG}}.md +// in current working directory. +// +// The content of this generated file is a keybindings cheatsheet. +// +// To generate cheatsheet in english run: +// LANG=en go run scripts/generate_cheatsheet.go + +package main + +import ( + "fmt" + "github.com/jesseduffield/lazygit/pkg/app" + "github.com/jesseduffield/lazygit/pkg/config" + "log" + "os" + "strings" +) + +func writeString(file *os.File, str string) { + _, err := file.WriteString(str) + if err != nil { + log.Fatal(err) + } +} + +func getTitle(mApp *app.App ,viewName string) string { + viewTitle := strings.Title(viewName) + "Title" + translatedTitle := mApp.Tr.SLocalize(viewTitle) + formattedTitle := fmt.Sprintf("\n## %s\n\n", translatedTitle) + return formattedTitle +} + +func main() { + mConfig, _ := config.NewAppConfig("", "", "", "", "", new(bool)) + mApp, _ := app.Setup(mConfig) + lang := mApp.Tr.GetLanguage() + file, _ := os.Create("Keybindings_" + lang + ".md") + current := "" + + writeString(file, fmt.Sprintf("# Lazygit %s\n", mApp.Tr.SLocalize("menu"))) + writeString(file, getTitle(mApp, "global")) + + writeString(file, "
\n")
+
+    for _, binding := range mApp.Gui.GetKeybindings() {
+        if binding.Description == "" {
+            continue
+        }
+
+        if binding.ViewName != current {
+            current = binding.ViewName
+            writeString(file, "
\n") + writeString(file, getTitle(mApp, current)) + writeString(file, "
\n")
+        }
+
+        info := fmt.Sprintf("  %s: %s\n", binding.GetKey(), binding.Description)
+        writeString(file, info)
+    }
+
+    writeString(file, "
\n") +} From 0dcfa09ff25384be1b926af85ba9a9a956681db4 Mon Sep 17 00:00:00 2001 From: Dawid Dziurla Date: Wed, 16 Jan 2019 18:54:54 +0100 Subject: [PATCH 114/118] run go fmt against generator --- scripts/generate_cheatsheet.go | 74 +++++++++++++++++----------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/scripts/generate_cheatsheet.go b/scripts/generate_cheatsheet.go index 466d26e3d..f427c29fa 100644 --- a/scripts/generate_cheatsheet.go +++ b/scripts/generate_cheatsheet.go @@ -9,55 +9,55 @@ package main import ( - "fmt" - "github.com/jesseduffield/lazygit/pkg/app" - "github.com/jesseduffield/lazygit/pkg/config" - "log" - "os" - "strings" + "fmt" + "github.com/jesseduffield/lazygit/pkg/app" + "github.com/jesseduffield/lazygit/pkg/config" + "log" + "os" + "strings" ) func writeString(file *os.File, str string) { - _, err := file.WriteString(str) - if err != nil { - log.Fatal(err) - } + _, err := file.WriteString(str) + if err != nil { + log.Fatal(err) + } } -func getTitle(mApp *app.App ,viewName string) string { - viewTitle := strings.Title(viewName) + "Title" - translatedTitle := mApp.Tr.SLocalize(viewTitle) - formattedTitle := fmt.Sprintf("\n## %s\n\n", translatedTitle) - return formattedTitle +func getTitle(mApp *app.App, viewName string) string { + viewTitle := strings.Title(viewName) + "Title" + translatedTitle := mApp.Tr.SLocalize(viewTitle) + formattedTitle := fmt.Sprintf("\n## %s\n\n", translatedTitle) + return formattedTitle } func main() { - mConfig, _ := config.NewAppConfig("", "", "", "", "", new(bool)) - mApp, _ := app.Setup(mConfig) - lang := mApp.Tr.GetLanguage() - file, _ := os.Create("Keybindings_" + lang + ".md") - current := "" + mConfig, _ := config.NewAppConfig("", "", "", "", "", new(bool)) + mApp, _ := app.Setup(mConfig) + lang := mApp.Tr.GetLanguage() + file, _ := os.Create("Keybindings_" + lang + ".md") + current := "" - writeString(file, fmt.Sprintf("# Lazygit %s\n", mApp.Tr.SLocalize("menu"))) - writeString(file, getTitle(mApp, "global")) + writeString(file, fmt.Sprintf("# Lazygit %s\n", mApp.Tr.SLocalize("menu"))) + writeString(file, getTitle(mApp, "global")) - writeString(file, "
\n")
+	writeString(file, "
\n")
 
-    for _, binding := range mApp.Gui.GetKeybindings() {
-        if binding.Description == "" {
-            continue
-        }
+	for _, binding := range mApp.Gui.GetKeybindings() {
+		if binding.Description == "" {
+			continue
+		}
 
-        if binding.ViewName != current {
-            current = binding.ViewName
-            writeString(file, "
\n") - writeString(file, getTitle(mApp, current)) - writeString(file, "
\n")
-        }
+		if binding.ViewName != current {
+			current = binding.ViewName
+			writeString(file, "
\n") + writeString(file, getTitle(mApp, current)) + writeString(file, "
\n")
+		}
 
-        info := fmt.Sprintf("  %s: %s\n", binding.GetKey(), binding.Description)
-        writeString(file, info)
-    }
+		info := fmt.Sprintf("  %s: %s\n", binding.GetKey(), binding.Description)
+		writeString(file, info)
+	}
 
-    writeString(file, "
\n") + writeString(file, "
\n") } From 2dc5e6d50356846fa30cd72f442452a7db9b1203 Mon Sep 17 00:00:00 2001 From: KOREAN139 Date: Wed, 26 Dec 2018 20:39:16 +0900 Subject: [PATCH 115/118] Fix recent repo view size issue getMessageHeight() calculates height under assumption that given view's wrap option (view.Wrap) is true, and createMenu() does not set wrap option as true. this causes gocui set improper view's height when lines in view needs to be wrapped. add *gocui.View as parameter in getMessageHeight(), and calculates view's height depend on its wrap option. resolve issue #354 --- pkg/gui/confirmation_panel.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index cdb01466a..36c0a017e 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -37,11 +37,16 @@ func (gui *Gui) closeConfirmationPrompt(g *gocui.Gui) error { return g.DeleteView("confirmation") } -func (gui *Gui) getMessageHeight(message string, width int) int { +func (gui *Gui) getMessageHeight(v *gocui.View, message string, width int) int { lines := strings.Split(message, "\n") lineCount := 0 - for _, line := range lines { - lineCount += len(line)/width + 1 + // if we need to wrap, calculate height to fit content within view's width + if v.Wrap { + for _, line := range lines { + lineCount += len(line)/width + 1 + } + } else { + lineCount = len(lines) } return lineCount } @@ -49,7 +54,8 @@ func (gui *Gui) getMessageHeight(message string, width int) int { func (gui *Gui) getConfirmationPanelDimensions(g *gocui.Gui, prompt string) (int, int, int, int) { width, height := g.Size() panelWidth := width / 2 - panelHeight := gui.getMessageHeight(prompt, panelWidth) + view := g.CurrentView() + panelHeight := gui.getMessageHeight(view, prompt, panelWidth) return width/2 - panelWidth/2, height/2 - panelHeight/2 - panelHeight%2 - 1, width/2 + panelWidth/2, From a38d1a3b689b204f3920dcfc3949bbd7c5c61f44 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 14 Jan 2019 20:25:01 +0900 Subject: [PATCH 116/118] Explicitly refer confirmation panel view as @jesseduffield pointed in #358, need to refer confirmation panel view explicitly in case something else has focus Co-Authored-By: KOREAN139 --- pkg/gui/confirmation_panel.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index 36c0a017e..ef17ccf67 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -54,7 +54,10 @@ func (gui *Gui) getMessageHeight(v *gocui.View, message string, width int) int { func (gui *Gui) getConfirmationPanelDimensions(g *gocui.Gui, prompt string) (int, int, int, int) { width, height := g.Size() panelWidth := width / 2 - view := g.CurrentView() + view, err := gui.g.View("confirmation") + if err != nil { // confirmation panel was deleted so we just return empty values + return 0,0,0,0 + } panelHeight := gui.getMessageHeight(view, prompt, panelWidth) return width/2 - panelWidth/2, height/2 - panelHeight/2 - panelHeight%2 - 1, From 695b092c4120d41c15ad969a6ea472e38379ebbd Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 15 Jan 2019 19:33:42 +1100 Subject: [PATCH 117/118] Directly send wrap argument rather than the view --- pkg/gui/confirmation_panel.go | 14 +++++--------- pkg/gui/menu_panel.go | 2 +- pkg/gui/view_helpers.go | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go index ef17ccf67..c3f33d429 100644 --- a/pkg/gui/confirmation_panel.go +++ b/pkg/gui/confirmation_panel.go @@ -37,11 +37,11 @@ func (gui *Gui) closeConfirmationPrompt(g *gocui.Gui) error { return g.DeleteView("confirmation") } -func (gui *Gui) getMessageHeight(v *gocui.View, message string, width int) int { +func (gui *Gui) getMessageHeight(wrap bool, message string, width int) int { lines := strings.Split(message, "\n") lineCount := 0 // if we need to wrap, calculate height to fit content within view's width - if v.Wrap { + if wrap { for _, line := range lines { lineCount += len(line)/width + 1 } @@ -51,14 +51,10 @@ func (gui *Gui) getMessageHeight(v *gocui.View, message string, width int) int { return lineCount } -func (gui *Gui) getConfirmationPanelDimensions(g *gocui.Gui, prompt string) (int, int, int, int) { +func (gui *Gui) getConfirmationPanelDimensions(g *gocui.Gui, wrap bool, prompt string) (int, int, int, int) { width, height := g.Size() panelWidth := width / 2 - view, err := gui.g.View("confirmation") - if err != nil { // confirmation panel was deleted so we just return empty values - return 0,0,0,0 - } - panelHeight := gui.getMessageHeight(view, prompt, panelWidth) + panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth) return width/2 - panelWidth/2, height/2 - panelHeight/2 - panelHeight%2 - 1, width/2 + panelWidth/2, @@ -76,7 +72,7 @@ func (gui *Gui) createPromptPanel(g *gocui.Gui, currentView *gocui.View, title s } func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt string) (*gocui.View, error) { - x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, prompt) + x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, true, prompt) confirmationView, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0) if err != nil { if err != gocui.ErrUnknownView { diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 8ce60f6be..5da80afa2 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -56,7 +56,7 @@ func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error return err } - x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, list) + x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, false, list) menuView, _ := gui.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 75270e388..a88f6ce3d 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -317,7 +317,7 @@ func (gui *Gui) resizePopupPanel(g *gocui.Gui, v *gocui.View) error { // If the confirmation panel is already displayed, just resize the width, // otherwise continue content := v.Buffer() - x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, content) + x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, v.Wrap, content) vx0, vy0, vx1, vy1 := v.Dimensions() if vx0 == x0 && vy0 == y0 && vx1 == x1 && vy1 == y1 { return nil From 3a607061a2303d9f45d308de652fbebe7300b43c Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 15 Jan 2019 20:12:31 +1100 Subject: [PATCH 118/118] Only reset origin in main view when handling the selection of a new file --- pkg/gui/files_panel.go | 26 ++++++++++++++++---------- pkg/gui/gui.go | 2 +- pkg/gui/view_helpers.go | 32 +++++++++++++++++--------------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index a89138161..f66cabe4a 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -26,7 +26,7 @@ func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) { return gui.State.Files[selectedLine], nil } -func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error { +func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View, alreadySelected bool) error { file, err := gui.getSelectedFile(g) if err != nil { if err != gui.Errors.ErrNoFiles { @@ -44,10 +44,18 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error { } content := gui.GitCommand.Diff(file, false) + if alreadySelected { + g.Update(func(*gocui.Gui) error { + return gui.setViewContent(gui.g, gui.getMainView(gui.g), content) + }) + return nil + } return gui.renderString(g, "main", content) } func (gui *Gui) refreshFiles(g *gocui.Gui) error { + selectedFile, _ := gui.getSelectedFile(gui.g) + filesView, err := g.View("files") if err != nil { return err @@ -64,7 +72,9 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error { fmt.Fprint(filesView, list) if filesView == g.CurrentView() { - return gui.handleFileSelect(g, filesView) + newSelectedFile, _ := gui.getSelectedFile(gui.g) + alreadySelected := newSelectedFile.Name == selectedFile.Name + return gui.handleFileSelect(g, filesView, alreadySelected) } return nil }) @@ -76,14 +86,14 @@ func (gui *Gui) handleFilesNextLine(g *gocui.Gui, v *gocui.View) error { panelState := gui.State.Panels.Files gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), false) - return gui.handleFileSelect(gui.g, v) + return gui.handleFileSelect(gui.g, v, false) } func (gui *Gui) handleFilesPrevLine(g *gocui.Gui, v *gocui.View) error { panelState := gui.State.Panels.Files gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), true) - return gui.handleFileSelect(gui.g, v) + return gui.handleFileSelect(gui.g, v, false) } // specific functions @@ -163,7 +173,7 @@ func (gui *Gui) handleFilePress(g *gocui.Gui, v *gocui.View) error { return err } - return gui.handleFileSelect(g, v) + return gui.handleFileSelect(g, v, true) } func (gui *Gui) allFilesStaged() bool { @@ -186,11 +196,7 @@ func (gui *Gui) handleStageAll(g *gocui.Gui, v *gocui.View) error { _ = gui.createErrorPanel(g, err.Error()) } - if err := gui.refreshFiles(g); err != nil { - return err - } - - return gui.handleFileSelect(g, v) + return gui.refreshFiles(g) } func (gui *Gui) handleAddPatch(g *gocui.Gui, v *gocui.View) error { diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 8ead65be3..ed7cfba0f 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -462,7 +462,7 @@ func (gui *Gui) updateLoader(g *gocui.Gui) error { content := gui.trimmedContent(view) if strings.Contains(content, "...") { staticContent := strings.Split(content, "...")[0] + "..." - if err := gui.synchronousRenderString(g, "confirmation", staticContent+" "+utils.Loader()); err != nil { + if err := gui.setViewContent(g, view, staticContent+" "+utils.Loader()); err != nil { return err } } diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index a88f6ce3d..7979d5e9b 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -90,7 +90,7 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error { case "status": return gui.handleStatusSelect(g, v) case "files": - return gui.handleFileSelect(g, v) + return gui.handleFileSelect(g, v, false) case "branches": return gui.handleBranchSelect(g, v) case "commits": @@ -222,26 +222,28 @@ func (gui *Gui) focusPoint(cx int, cy int, v *gocui.View) error { return nil } -func (gui *Gui) synchronousRenderString(g *gocui.Gui, viewName, s string) error { - v, err := g.View(viewName) - // just in case the view disappeared as this function was called, we'll - // silently return if it's not found - if err != nil { - return nil - } - v.Clear() - if err := v.SetOrigin(0, 0); err != nil { - return err - } +func (gui *Gui) cleanString(s string) string { output := string(bom.Clean([]byte(s))) - output = utils.NormalizeLinefeeds(output) - fmt.Fprint(v, output) + return utils.NormalizeLinefeeds(output) +} + +func (gui *Gui) setViewContent(g *gocui.Gui, v *gocui.View, s string) error { + v.Clear() + fmt.Fprint(v, gui.cleanString(s)) return nil } +// renderString resets the origin of a view and sets its content func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error { g.Update(func(*gocui.Gui) error { - return gui.synchronousRenderString(gui.g, viewName, s) + v, err := g.View(viewName) + if err != nil { + return nil // return gracefully if view has been deleted + } + if err := v.SetOrigin(0, 0); err != nil { + return err + } + return gui.setViewContent(gui.g, v, s) }) return nil }