Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d15982c07 |
@@ -17,16 +17,16 @@ builds:
|
||||
ldflags:
|
||||
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.buildSource=binaryRelease
|
||||
|
||||
archives:
|
||||
- replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: 32-bit
|
||||
amd64: x86_64
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
archive:
|
||||
replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: 32-bit
|
||||
amd64: x86_64
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
|
||||
@@ -21,7 +21,6 @@ Default path for the config file: `~/.config/jesseduffield/lazygit/config.yml`
|
||||
commitLength:
|
||||
show: true
|
||||
mouseEvents: true
|
||||
skipUnstageLineWarning: false
|
||||
git:
|
||||
merging:
|
||||
# only applicable to unix users
|
||||
@@ -68,8 +67,8 @@ Default path for the config file: `~/.config/jesseduffield/lazygit/config.yml`
|
||||
pullFiles: 'p'
|
||||
refresh: 'R'
|
||||
createPatchOptionsMenu: '<c-p>'
|
||||
nextTab: ']'
|
||||
prevTab: '['
|
||||
nextBranchTab: ']'
|
||||
prevBranchTab: '['
|
||||
status:
|
||||
checkForUpdate: 'u'
|
||||
recentRepos: '<enter>'
|
||||
@@ -113,7 +112,7 @@ Default path for the config file: `~/.config/jesseduffield/lazygit/config.yml`
|
||||
cherryPickCopyRange: 'C'
|
||||
pasteCommits: 'v'
|
||||
tagCommit: 'T'
|
||||
toggleDiffCommit: 'i'
|
||||
toggleDiffCommit: 'h'
|
||||
checkoutCommit: '<space>'
|
||||
stash:
|
||||
popStash: 'g'
|
||||
@@ -197,7 +196,7 @@ If you have issues with a light terminal theme where you can't read / see the te
|
||||

|
||||
|
||||
## Keybindings:
|
||||
For all possible keybinding options, check [Custom_Keybinding.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybinding.md)
|
||||
For all possible keybinding options, check [Custom_Keybinding.md](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybinding.md) <++>
|
||||
|
||||
|
||||
#### Example Keybindings For Colemak Users:
|
||||
@@ -220,8 +219,5 @@ For all possible keybinding options, check [Custom_Keybinding.md](https://github
|
||||
commits:
|
||||
moveDownCommit: '<c-e>'
|
||||
moveUpCommit: '<c-u>'
|
||||
toggleDiffCommit: 'l'
|
||||
branches:
|
||||
viewGitFlowOptions: 'I'
|
||||
```
|
||||
|
||||
|
||||
8
go.mod
8
go.mod
@@ -11,15 +11,15 @@ require (
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20191116013947-b13bda319532
|
||||
github.com/jesseduffield/pty v1.2.1
|
||||
github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7 // indirect
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9 // indirect
|
||||
github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20190630083001-9dd53af7214e // indirect
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.11 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.8
|
||||
github.com/mattn/go-runewidth v0.0.7
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.3
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
|
||||
12
go.sum
12
go.sum
@@ -79,14 +79,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20191116013947-b13bda319532 h1:V1Lk2rm5/p27NjnlF2ezzkxDaisHNcveMNueSD7RYgs=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20191116013947-b13bda319532/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200112025325-6c933915c351 h1:+sSqd2YotacWt+1MNRN8ZmXnYoiJeblZeptzKiHIyv0=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200112025325-6c933915c351/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200131125953-f679540a7039 h1:CVhilJ8ZdN7GmAI+fbH9829Cp/8hbK7Lijbd4VaNgo0=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200131125953-f679540a7039/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200131131454-a319843434ac h1:vp7I0RpFq4L46nFA9QQokzhFgr68LRGtwDO9xfq4F+A=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200131131454-a319843434ac/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5 h1:tE0w3tuL/bj1o5VMhjjE0ep6i7Fva+RYjKcMFcniJEY=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/pty v1.2.1 h1:7xYBiwNH0PpWqC8JmvrPq1a/ksNqyCavzWu9pbBGYWI=
|
||||
github.com/jesseduffield/pty v1.2.1/go.mod h1:7jlS40+UhOqkZJDIG1B/H21xnuET/+fvbbnHCa8wSIo=
|
||||
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00 h1:+JaOkfBNYQYlGD7dgru8mCwYNEc5tRRI8mThlVANhSM=
|
||||
@@ -95,8 +87,6 @@ github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7 h1:CRD7bVjlG
|
||||
github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7/go.mod h1:VspA3aTkEo0Q7TPCLmX1uHNP+Wb4iSDX09hmTRo1QYc=
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20190630083001-9dd53af7214e h1:tth7wr6+sfSbdpRWWrwvLYyS56HyIRVfq0Qcl2h28wM=
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20190630083001-9dd53af7214e/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4=
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9 h1:iBBk1lhFwjwJw//J2m1yyz9S368GeXQTpMVACTyQMh0=
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
@@ -128,8 +118,6 @@ github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGe
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||
"github.com/jesseduffield/rollrus"
|
||||
"github.com/shibukawa/configdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -72,7 +73,9 @@ func newDevelopmentLogger(config config.AppConfigurer) *logrus.Logger {
|
||||
|
||||
func newLogger(config config.AppConfigurer) *logrus.Entry {
|
||||
var log *logrus.Logger
|
||||
environment := "production"
|
||||
if config.GetDebug() || os.Getenv("DEBUG") == "TRUE" {
|
||||
environment = "development"
|
||||
log = newDevelopmentLogger(config)
|
||||
} else {
|
||||
log = newProductionLogger(config)
|
||||
@@ -82,6 +85,11 @@ func newLogger(config config.AppConfigurer) *logrus.Entry {
|
||||
// https://github.com/aybabtme/humanlog
|
||||
log.Formatter = &logrus.JSONFormatter{}
|
||||
|
||||
if config.GetUserConfig().GetString("reporting") == "on" {
|
||||
// this isn't really a secret token: it only has permission to push new rollbar items
|
||||
hook := rollrus.NewHook("23432119147a4367abf7c0de2aa99a2d", environment)
|
||||
log.Hooks.Add(hook)
|
||||
}
|
||||
return log.WithFields(logrus.Fields{
|
||||
"debug": config.GetDebug(),
|
||||
"version": config.GetVersion(),
|
||||
|
||||
@@ -48,7 +48,7 @@ func (b *BranchListBuilder) obtainCurrentBranch() *Branch {
|
||||
func (b *BranchListBuilder) obtainReflogBranches() []*Branch {
|
||||
branches := make([]*Branch, 0)
|
||||
// if we directly put this string in RunCommandWithOutput the compiler complains because it thinks it's a format string
|
||||
unescaped := "git reflog --date=relative --pretty='%gd|%gs' --grep-reflog='checkout: moving' HEAD"
|
||||
unescaped := "git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD"
|
||||
rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput(unescaped)
|
||||
if err != nil {
|
||||
return branches
|
||||
@@ -56,11 +56,9 @@ func (b *BranchListBuilder) obtainReflogBranches() []*Branch {
|
||||
|
||||
branchLines := utils.SplitLines(rawString)
|
||||
for _, line := range branchLines {
|
||||
recency, branchName := branchInfoFromLine(line)
|
||||
if branchName == "" {
|
||||
continue
|
||||
}
|
||||
branch := &Branch{Name: branchName, Recency: recency}
|
||||
timeNumber, timeUnit, branchName := branchInfoFromLine(line)
|
||||
timeUnit = abbreviatedTimeUnit(timeUnit)
|
||||
branch := &Branch{Name: branchName, Recency: timeNumber + timeUnit}
|
||||
branches = append(branches, branch)
|
||||
}
|
||||
return uniqueByName(branches)
|
||||
@@ -145,17 +143,11 @@ func uniqueByName(branches []*Branch) []*Branch {
|
||||
|
||||
// A line will have the form '10 days ago master' so we need to strip out the
|
||||
// useful information from that into timeNumber, timeUnit, and branchName
|
||||
func branchInfoFromLine(line string) (string, string) {
|
||||
// example line: HEAD@{12 minutes ago}|checkout: moving from pulling-from-forks to tim77-patch-1
|
||||
r := regexp.MustCompile(`HEAD\@\{([^\s]+) ([^\s]+) ago\}\|.*?([^\s]*)$`)
|
||||
matches := r.FindStringSubmatch(strings.TrimSpace(line))
|
||||
if len(matches) == 0 {
|
||||
return "", ""
|
||||
}
|
||||
since := matches[1]
|
||||
unit := matches[2]
|
||||
branchName := matches[3]
|
||||
return since + abbreviatedTimeUnit(unit), branchName
|
||||
func branchInfoFromLine(line string) (string, string, string) {
|
||||
r := regexp.MustCompile("\\|.*\\s")
|
||||
line = r.ReplaceAllString(line, " ")
|
||||
words := strings.Split(line, " ")
|
||||
return words[0], words[1], words[len(words)-1]
|
||||
}
|
||||
|
||||
func abbreviatedTimeUnit(timeUnit string) string {
|
||||
|
||||
@@ -44,8 +44,6 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
|
||||
shaColor = green
|
||||
case "rebasing":
|
||||
shaColor = blue
|
||||
case "reflog":
|
||||
shaColor = blue
|
||||
case "selected":
|
||||
shaColor = magenta
|
||||
default:
|
||||
|
||||
@@ -46,7 +46,7 @@ func NewCommitListBuilder(log *logrus.Entry, gitCommand *GitCommand, osCommand *
|
||||
}
|
||||
|
||||
// GetCommits obtains the commits of the current branch
|
||||
func (c *CommitListBuilder) GetCommits(limit bool) ([]*Commit, error) {
|
||||
func (c *CommitListBuilder) GetCommits() ([]*Commit, error) {
|
||||
commits := []*Commit{}
|
||||
var rebasingCommits []*Commit
|
||||
rebaseMode, err := c.GitCommand.RebaseMode()
|
||||
@@ -65,7 +65,7 @@ func (c *CommitListBuilder) GetCommits(limit bool) ([]*Commit, error) {
|
||||
}
|
||||
|
||||
unpushedCommits := c.getUnpushedCommits()
|
||||
log := c.getLog(limit)
|
||||
log := c.getLog()
|
||||
|
||||
// now we can split it up and turn it into commits
|
||||
for _, line := range utils.SplitLines(log) {
|
||||
@@ -281,15 +281,12 @@ func (c *CommitListBuilder) getUnpushedCommits() map[string]bool {
|
||||
return pushables
|
||||
}
|
||||
|
||||
// getLog gets the git log.
|
||||
func (c *CommitListBuilder) getLog(limit bool) string {
|
||||
limitFlag := ""
|
||||
if limit {
|
||||
limitFlag = "-30"
|
||||
}
|
||||
|
||||
c.Log.Warn(fmt.Sprintf("git log --oneline %s", limitFlag))
|
||||
result, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --oneline %s", limitFlag))
|
||||
// getLog gets the git log (currently limited to 30 commits for performance
|
||||
// until we work out lazy loading
|
||||
func (c *CommitListBuilder) getLog() string {
|
||||
// currently limiting to 30 for performance reasons
|
||||
// TODO: add lazyloading when you scroll down
|
||||
result, err := c.OSCommand.RunCommandWithOutput("git log --oneline -30")
|
||||
if err != nil {
|
||||
// assume if there is an error there are no commits yet for this branch
|
||||
return ""
|
||||
|
||||
@@ -188,7 +188,7 @@ func TestCommitListBuilderGetLog(t *testing.T) {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
c := NewDummyCommitListBuilder()
|
||||
c.OSCommand.SetCommand(s.command)
|
||||
s.test(c.getLog(true))
|
||||
s.test(c.getLog())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -312,7 +312,7 @@ func TestCommitListBuilderGetCommits(t *testing.T) {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
c := NewDummyCommitListBuilder()
|
||||
c.OSCommand.SetCommand(s.command)
|
||||
s.test(c.GetCommits(true))
|
||||
s.test(c.GetCommits())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,8 +176,8 @@ func stashEntryFromLine(line string, index int) *StashEntry {
|
||||
}
|
||||
|
||||
// GetStashEntryDiff stash diff
|
||||
func (c *GitCommand) ShowStashEntryCmdStr(index int) string {
|
||||
return fmt.Sprintf("git stash show -p --color stash@{%d}", index)
|
||||
func (c *GitCommand) GetStashEntryDiff(index int) (string, error) {
|
||||
return c.OSCommand.RunCommandWithOutput("git stash show -p --color stash@{%d}", index)
|
||||
}
|
||||
|
||||
// GetStatusFiles git status files
|
||||
@@ -191,10 +191,10 @@ func (c *GitCommand) GetStatusFiles() []*File {
|
||||
stagedChange := change[0:1]
|
||||
unstagedChange := statusString[1:2]
|
||||
filename := c.OSCommand.Unquote(statusString[3:])
|
||||
untracked := utils.IncludesString([]string{"??", "A ", "AM"}, change)
|
||||
hasNoStagedChanges := utils.IncludesString([]string{" ", "U", "?"}, stagedChange)
|
||||
hasMergeConflicts := utils.IncludesString([]string{"DD", "AA", "UU", "AU", "UA", "UD", "DU"}, change)
|
||||
hasInlineMergeConflicts := utils.IncludesString([]string{"UU", "AA"}, change)
|
||||
_, untracked := map[string]bool{"??": true, "A ": true, "AM": true}[change]
|
||||
_, hasNoStagedChanges := map[string]bool{" ": true, "U": true, "?": true}[stagedChange]
|
||||
hasMergeConflicts := change == "UU" || change == "AA" || change == "DU"
|
||||
hasInlineMergeConflicts := change == "UU" || change == "AA"
|
||||
|
||||
file := &File{
|
||||
Name: filename,
|
||||
@@ -404,12 +404,12 @@ func (c *GitCommand) AmendHead() (*exec.Cmd, error) {
|
||||
}
|
||||
|
||||
// Pull pulls from repo
|
||||
func (c *GitCommand) Pull(args string, ask func(string) string) error {
|
||||
return c.OSCommand.DetectUnamePass("git pull --no-edit "+args, ask)
|
||||
func (c *GitCommand) Pull(ask func(string) string) error {
|
||||
return c.OSCommand.DetectUnamePass("git pull --no-edit", ask)
|
||||
}
|
||||
|
||||
// Push pushes to a branch
|
||||
func (c *GitCommand) Push(branchName string, force bool, upstream string, args string, ask func(string) string) error {
|
||||
func (c *GitCommand) Push(branchName string, force bool, upstream string, ask func(string) string) error {
|
||||
forceFlag := ""
|
||||
if force {
|
||||
forceFlag = "--force-with-lease"
|
||||
@@ -420,7 +420,7 @@ func (c *GitCommand) Push(branchName string, force bool, upstream string, args s
|
||||
setUpstreamArg = "--set-upstream " + upstream
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf("git push --follow-tags %s %s %s", forceFlag, setUpstreamArg, args)
|
||||
cmd := fmt.Sprintf("git push --follow-tags %s %s", forceFlag, setUpstreamArg)
|
||||
return c.OSCommand.DetectUnamePass(cmd, ask)
|
||||
}
|
||||
|
||||
@@ -538,8 +538,7 @@ func (c *GitCommand) PrepareCommitAmendSubProcess() *exec.Cmd {
|
||||
// Currently it limits the result to 100 commits, but when we get async stuff
|
||||
// working we can do lazy loading
|
||||
func (c *GitCommand) GetBranchGraph(branchName string) (string, error) {
|
||||
cmdStr := c.GetBranchGraphCmdStr(branchName)
|
||||
return c.OSCommand.RunCommandWithOutput(cmdStr)
|
||||
return c.OSCommand.RunCommandWithOutput("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 %s", branchName)
|
||||
}
|
||||
|
||||
func (c *GitCommand) GetUpstreamForBranch(branchName string) (string, error) {
|
||||
@@ -552,12 +551,41 @@ func (c *GitCommand) Ignore(filename string) error {
|
||||
return c.OSCommand.AppendLineToFile(".gitignore", filename)
|
||||
}
|
||||
|
||||
func (c *GitCommand) ShowCmdStr(sha string) string {
|
||||
return fmt.Sprintf("git show --color --no-renames %s", sha)
|
||||
}
|
||||
// Show shows the diff of a commit
|
||||
func (c *GitCommand) Show(sha string) (string, error) {
|
||||
show, err := c.OSCommand.RunCommandWithOutput("git show --color --no-renames %s", sha)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (c *GitCommand) GetBranchGraphCmdStr(branchName string) string {
|
||||
return fmt.Sprintf("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium %s", branchName)
|
||||
// if this is a merge commit, we need to go a step further and get the diff between the two branches we merged
|
||||
revList, err := c.OSCommand.RunCommandWithOutput("git rev-list -1 --merges %s^...%s", sha, sha)
|
||||
if err != nil {
|
||||
// turns out we get an error here when it's the first commit. We'll just return the original show
|
||||
return show, nil
|
||||
}
|
||||
if len(revList) == 0 {
|
||||
return show, nil
|
||||
}
|
||||
|
||||
// we want to pull out 1a6a69a and 3b51d7c from this:
|
||||
// commit ccc771d8b13d5b0d4635db4463556366470fd4f6
|
||||
// Merge: 1a6a69a 3b51d7c
|
||||
lines := utils.SplitLines(show)
|
||||
if len(lines) < 2 {
|
||||
return show, nil
|
||||
}
|
||||
|
||||
secondLineWords := strings.Split(lines[1], " ")
|
||||
if len(secondLineWords) < 3 {
|
||||
return show, nil
|
||||
}
|
||||
|
||||
mergeDiff, err := c.OSCommand.RunCommandWithOutput("git diff --color %s...%s", secondLineWords[1], secondLineWords[2])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return show + mergeDiff, nil
|
||||
}
|
||||
|
||||
// GetRemoteURL returns current repo remote url
|
||||
@@ -578,12 +606,6 @@ func (c *GitCommand) CheckRemoteBranchExists(branch *Branch) bool {
|
||||
|
||||
// Diff returns the diff of a file
|
||||
func (c *GitCommand) Diff(file *File, plain bool, cached bool) string {
|
||||
// for now we assume an error means the file was deleted
|
||||
s, _ := c.OSCommand.RunCommandWithOutput(c.DiffCmdStr(file, plain, cached))
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *GitCommand) DiffCmdStr(file *File, plain bool, cached bool) string {
|
||||
cachedArg := ""
|
||||
trackedArg := "--"
|
||||
colorArg := "--color"
|
||||
@@ -599,7 +621,9 @@ func (c *GitCommand) DiffCmdStr(file *File, plain bool, cached bool) string {
|
||||
colorArg = ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("git diff %s %s %s %s", colorArg, cachedArg, trackedArg, fileName)
|
||||
// for now we assume an error means the file was deleted
|
||||
s, _ := c.OSCommand.RunCommandWithOutput("git diff %s %s %s %s", colorArg, cachedArg, trackedArg, fileName)
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
|
||||
@@ -623,12 +647,10 @@ func (c *GitCommand) FastForward(branchName string, remoteName string, remoteBra
|
||||
|
||||
func (c *GitCommand) RunSkipEditorCommand(command string) error {
|
||||
cmd := c.OSCommand.ExecutableFromString(command)
|
||||
lazyGitPath := c.OSCommand.GetLazygitPath()
|
||||
cmd.Env = append(
|
||||
cmd.Env,
|
||||
"LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY",
|
||||
"EDITOR="+lazyGitPath,
|
||||
"VISUAL="+lazyGitPath,
|
||||
"EDITOR="+c.OSCommand.GetLazygitPath(),
|
||||
)
|
||||
return c.OSCommand.RunExecutable(cmd)
|
||||
}
|
||||
@@ -886,17 +908,11 @@ func (c *GitCommand) GetCommitFiles(commitSha string, patchManager *PatchManager
|
||||
|
||||
// ShowCommitFile get the diff of specified commit file
|
||||
func (c *GitCommand) ShowCommitFile(commitSha, fileName string, plain bool) (string, error) {
|
||||
cmdStr := c.ShowCommitFileCmdStr(commitSha, fileName, plain)
|
||||
return c.OSCommand.RunCommandWithOutput(cmdStr)
|
||||
}
|
||||
|
||||
func (c *GitCommand) ShowCommitFileCmdStr(commitSha, fileName string, plain bool) string {
|
||||
colorArg := "--color"
|
||||
if plain {
|
||||
colorArg = ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("git show --no-renames %s %s -- %s", colorArg, commitSha, fileName)
|
||||
return c.OSCommand.RunCommandWithOutput("git show --no-renames %s %s -- %s", colorArg, commitSha, fileName)
|
||||
}
|
||||
|
||||
// CheckoutFile checks out the file for the given commit
|
||||
@@ -1082,6 +1098,10 @@ func (c *GitCommand) CreateLightweightTag(tagName string, commitSha string) erro
|
||||
return c.OSCommand.RunCommand("git tag %s %s", tagName, commitSha)
|
||||
}
|
||||
|
||||
func (c *GitCommand) ShowTag(tagName string) (string, error) {
|
||||
return c.OSCommand.RunCommandWithOutput("git tag -n99 %s", tagName)
|
||||
}
|
||||
|
||||
func (c *GitCommand) DeleteTag(tagName string) error {
|
||||
return c.OSCommand.RunCommand("git tag -d %s", tagName)
|
||||
}
|
||||
@@ -1093,28 +1113,3 @@ func (c *GitCommand) PushTag(remoteName string, tagName string) error {
|
||||
func (c *GitCommand) FetchRemote(remoteName string) error {
|
||||
return c.OSCommand.RunCommand("git fetch %s", remoteName)
|
||||
}
|
||||
|
||||
func (c *GitCommand) GetReflogCommits() ([]*Commit, error) {
|
||||
output, err := c.OSCommand.RunCommandWithOutput("git reflog")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(output), "\n")
|
||||
commits := make([]*Commit, len(lines))
|
||||
re := regexp.MustCompile(`(\w+).*HEAD@\{\d+\}: (.*)`)
|
||||
for i, line := range lines {
|
||||
match := re.FindStringSubmatch(line)
|
||||
if len(match) == 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
commits[i] = &Commit{
|
||||
Sha: match[1],
|
||||
Name: match[2],
|
||||
Status: "reflog",
|
||||
}
|
||||
}
|
||||
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -319,6 +318,21 @@ func TestGitCommandGetStashEntries(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGitCommandGetStashEntryDiff is a function.
|
||||
func TestGitCommandGetStashEntryDiff(t *testing.T) {
|
||||
gitCmd := NewDummyGitCommand()
|
||||
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
|
||||
assert.EqualValues(t, "git", cmd)
|
||||
assert.EqualValues(t, []string{"stash", "show", "-p", "--color", "stash@{1}"}, args)
|
||||
|
||||
return exec.Command("echo")
|
||||
}
|
||||
|
||||
_, err := gitCmd.GetStashEntryDiff(1)
|
||||
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestGitCommandGetStatusFiles is a function.
|
||||
func TestGitCommandGetStatusFiles(t *testing.T) {
|
||||
type scenario struct {
|
||||
@@ -1016,7 +1030,7 @@ func TestGitCommandPush(t *testing.T) {
|
||||
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 {
|
||||
err := gitCmd.Push("test", s.forcePush, "", func(passOrUname string) string {
|
||||
return "\n"
|
||||
})
|
||||
s.test(err)
|
||||
@@ -1397,6 +1411,66 @@ func TestGitCommandDiscardAllFileChanges(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGitCommandShow is a function.
|
||||
func TestGitCommandShow(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
arg string
|
||||
command func(string, ...string) *exec.Cmd
|
||||
test func(string, error)
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"regular commit",
|
||||
"456abcde",
|
||||
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||||
{
|
||||
Expect: "git show --color --no-renames 456abcde",
|
||||
Replace: "echo \"commit ccc771d8b13d5b0d4635db4463556366470fd4f6\nblah\"",
|
||||
},
|
||||
{
|
||||
Expect: "git rev-list -1 --merges 456abcde^...456abcde",
|
||||
Replace: "echo",
|
||||
},
|
||||
}),
|
||||
func(result string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "commit ccc771d8b13d5b0d4635db4463556366470fd4f6\nblah\n", result)
|
||||
},
|
||||
},
|
||||
{
|
||||
"merge commit",
|
||||
"456abcde",
|
||||
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||||
{
|
||||
Expect: "git show --color --no-renames 456abcde",
|
||||
Replace: "echo \"commit ccc771d8b13d5b0d4635db4463556366470fd4f6\nMerge: 1a6a69a 3b51d7c\"",
|
||||
},
|
||||
{
|
||||
Expect: "git rev-list -1 --merges 456abcde^...456abcde",
|
||||
Replace: "echo aa30e006433628ba9281652952b34d8aacda9c01",
|
||||
},
|
||||
{
|
||||
Expect: "git diff --color 1a6a69a...3b51d7c",
|
||||
Replace: "echo blah",
|
||||
},
|
||||
}),
|
||||
func(result string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "commit ccc771d8b13d5b0d4635db4463556366470fd4f6\nMerge: 1a6a69a 3b51d7c\nblah\n", result)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
gitCmd := NewDummyGitCommand()
|
||||
|
||||
for _, s := range scenarios {
|
||||
gitCmd.OSCommand.command = s.command
|
||||
s.test(gitCmd.Show(s.arg))
|
||||
}
|
||||
}
|
||||
|
||||
// TestGitCommandCheckout is a function.
|
||||
func TestGitCommandCheckout(t *testing.T) {
|
||||
type scenario struct {
|
||||
@@ -1449,7 +1523,7 @@ func TestGitCommandGetBranchGraph(t *testing.T) {
|
||||
gitCmd := NewDummyGitCommand()
|
||||
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
|
||||
assert.EqualValues(t, "git", cmd)
|
||||
assert.EqualValues(t, []string{"log", "--graph", "--color", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "test"}, args)
|
||||
assert.EqualValues(t, []string{"log", "--graph", "--color", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "-100", "test"}, args)
|
||||
|
||||
return exec.Command("echo")
|
||||
}
|
||||
@@ -2102,37 +2176,6 @@ func TestGitCommandCreateFixupCommit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGitCommandSkipEditorCommand confirms that SkipEditorCommand injects
|
||||
// environment variables that suppress an interactive editor
|
||||
func TestGitCommandSkipEditorCommand(t *testing.T) {
|
||||
cmd := NewDummyGitCommand()
|
||||
|
||||
cmd.OSCommand.SetBeforeExecuteCmd(func(cmd *exec.Cmd) {
|
||||
test.AssertContainsMatch(
|
||||
t,
|
||||
cmd.Env,
|
||||
regexp.MustCompile("^VISUAL="),
|
||||
"expected VISUAL to be set for a non-interactive external command",
|
||||
)
|
||||
|
||||
test.AssertContainsMatch(
|
||||
t,
|
||||
cmd.Env,
|
||||
regexp.MustCompile("^EDITOR="),
|
||||
"expected EDITOR to be set for a non-interactive external command",
|
||||
)
|
||||
|
||||
test.AssertContainsMatch(
|
||||
t,
|
||||
cmd.Env,
|
||||
regexp.MustCompile("^LAZYGIT_CLIENT_COMMAND=EXIT_IMMEDIATELY$"),
|
||||
"expected LAZYGIT_CLIENT_COMMAND to be set for a non-interactive external command",
|
||||
)
|
||||
})
|
||||
|
||||
cmd.RunSkipEditorCommand("true")
|
||||
}
|
||||
|
||||
func TestFindDotGitDir(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
|
||||
@@ -36,7 +36,6 @@ type OSCommand struct {
|
||||
Platform *Platform
|
||||
Config config.AppConfigurer
|
||||
command func(string, ...string) *exec.Cmd
|
||||
beforeExecuteCmd func(*exec.Cmd)
|
||||
getGlobalGitConfig func(string) (string, error)
|
||||
getenv func(string) string
|
||||
}
|
||||
@@ -48,7 +47,6 @@ func NewOSCommand(log *logrus.Entry, config config.AppConfigurer) *OSCommand {
|
||||
Platform: getPlatform(),
|
||||
Config: config,
|
||||
command: exec.Command,
|
||||
beforeExecuteCmd: func(*exec.Cmd) {},
|
||||
getGlobalGitConfig: gitconfig.Global,
|
||||
getenv: os.Getenv,
|
||||
}
|
||||
@@ -60,10 +58,6 @@ func (c *OSCommand) SetCommand(cmd func(string, ...string) *exec.Cmd) {
|
||||
c.command = cmd
|
||||
}
|
||||
|
||||
func (c *OSCommand) SetBeforeExecuteCmd(cmd func(*exec.Cmd)) {
|
||||
c.beforeExecuteCmd = cmd
|
||||
}
|
||||
|
||||
// RunCommandWithOutput wrapper around commands returning their output and error
|
||||
// NOTE: If you don't pass any formatArgs we'll just use the command directly,
|
||||
// however there's a bizarre compiler error/warning when you pass in a formatString
|
||||
@@ -82,7 +76,6 @@ func (c *OSCommand) RunCommandWithOutput(formatString string, formatArgs ...inte
|
||||
|
||||
// RunExecutableWithOutput runs an executable file and returns its output
|
||||
func (c *OSCommand) RunExecutableWithOutput(cmd *exec.Cmd) (string, error) {
|
||||
c.beforeExecuteCmd(cmd)
|
||||
return sanitisedCommandOutput(cmd.CombinedOutput())
|
||||
}
|
||||
|
||||
@@ -315,7 +308,6 @@ func (c *OSCommand) FileExists(path string) (bool, error) {
|
||||
// this is useful if you need to give your command some environment variables
|
||||
// before running it
|
||||
func (c *OSCommand) RunPreparedCommand(cmd *exec.Cmd) error {
|
||||
c.beforeExecuteCmd(cmd)
|
||||
out, err := cmd.CombinedOutput()
|
||||
outString := string(out)
|
||||
c.Log.Info(outString)
|
||||
|
||||
@@ -243,7 +243,6 @@ func GetDefaultConfig() []byte {
|
||||
scrollHeight: 2
|
||||
scrollPastBottom: true
|
||||
mouseEvents: true
|
||||
skipUnstageLineWarning: false
|
||||
theme:
|
||||
lightTheme: false
|
||||
activeBorderColor:
|
||||
@@ -301,8 +300,8 @@ keybinding:
|
||||
pullFiles: 'p'
|
||||
refresh: 'R'
|
||||
createPatchOptionsMenu: '<c-p>'
|
||||
nextTab: ']'
|
||||
prevTab: '['
|
||||
nextBranchTab: ']'
|
||||
prevBranchTab: '['
|
||||
status:
|
||||
checkForUpdate: 'u'
|
||||
recentRepos: '<enter>'
|
||||
@@ -346,7 +345,7 @@ keybinding:
|
||||
cherryPickCopyRange: 'C'
|
||||
pasteCommits: 'v'
|
||||
tagCommit: 'T'
|
||||
toggleDiffCommit: 'i'
|
||||
toggleDiffCommit: 'h'
|
||||
checkoutCommit: '<space>'
|
||||
stash:
|
||||
popStash: 'g'
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
@@ -51,26 +49,15 @@ func (m *statusManager) getStatusString() string {
|
||||
// WithWaitingStatus wraps a function and shows a waiting status while the function is still executing
|
||||
func (gui *Gui) WithWaitingStatus(name string, f func() error) error {
|
||||
go func() {
|
||||
gui.statusManager.addWaitingStatus(name)
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
gui.statusManager.addWaitingStatus(name)
|
||||
return nil
|
||||
})
|
||||
|
||||
defer func() {
|
||||
defer gui.g.Update(func(g *gocui.Gui) error {
|
||||
gui.statusManager.removeStatus(name)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Millisecond * 50)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
appStatus := gui.statusManager.getStatusString()
|
||||
gui.Log.Warn(appStatus)
|
||||
if appStatus == "" {
|
||||
return
|
||||
}
|
||||
if err := gui.renderString(gui.g, "appStatus", appStatus); err != nil {
|
||||
gui.Log.Warn(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := f(); err != nil {
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -36,45 +37,40 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
// This really shouldn't happen: there should always be a master branch
|
||||
if len(gui.State.Branches) == 0 {
|
||||
return gui.newStringTask("main", gui.Tr.SLocalize("NoBranchesThisRepo"))
|
||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoBranchesThisRepo"))
|
||||
}
|
||||
branch := gui.getSelectedBranch()
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches), v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
|
||||
)
|
||||
if err := gui.newCmdTask("main", cmd); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
go func() {
|
||||
_ = gui.RenderSelectedBranchUpstreamDifferences()
|
||||
}()
|
||||
go func() {
|
||||
upstream, _ := gui.GitCommand.GetUpstreamForBranch(branch.Name)
|
||||
if strings.Contains(upstream, "no upstream configured for branch") || strings.Contains(upstream, "unknown revision or path not in the working tree") {
|
||||
upstream = gui.Tr.SLocalize("notTrackingRemote")
|
||||
}
|
||||
graph, err := gui.GitCommand.GetBranchGraph(branch.Name)
|
||||
if err != nil && strings.HasPrefix(graph, "fatal: ambiguous argument") {
|
||||
graph = gui.Tr.SLocalize("NoTrackingThisBranch")
|
||||
}
|
||||
_ = gui.renderString(g, "main", fmt.Sprintf("%s → %s\n\n%s", utils.ColoredString(branch.Name, color.FgGreen), utils.ColoredString(upstream, color.FgRed), graph))
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) RenderSelectedBranchUpstreamDifferences() error {
|
||||
return gui.newTask("branches", func(stop chan struct{}) error {
|
||||
// here we tell the selected branch that it is selected.
|
||||
// this is necessary for showing stats on a branch that is selected, because
|
||||
// the displaystring function doesn't have access to gui state to tell if it's selected
|
||||
for i, branch := range gui.State.Branches {
|
||||
branch.Selected = i == gui.State.Panels.Branches.SelectedLine
|
||||
}
|
||||
// here we tell the selected branch that it is selected.
|
||||
// this is necessary for showing stats on a branch that is selected, because
|
||||
// the displaystring function doesn't have access to gui state to tell if it's selected
|
||||
for i, branch := range gui.State.Branches {
|
||||
branch.Selected = i == gui.State.Panels.Branches.SelectedLine
|
||||
}
|
||||
|
||||
branch := gui.getSelectedBranch()
|
||||
branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
|
||||
|
||||
select {
|
||||
case <-stop:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
return gui.renderListPanel(gui.getBranchesView(), gui.State.Branches)
|
||||
})
|
||||
branch := gui.getSelectedBranch()
|
||||
branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
|
||||
return gui.renderListPanel(gui.getBranchesView(), gui.State.Branches)
|
||||
}
|
||||
|
||||
// gui.refreshStatus is called at the end of this because that's when we can
|
||||
@@ -115,10 +111,8 @@ func (gui *Gui) renderLocalBranchesWithSelection() error {
|
||||
if err := gui.renderListPanel(branchesView, gui.State.Branches); err != nil {
|
||||
return err
|
||||
}
|
||||
if gui.g.CurrentView() == branchesView && branchesView.Context == "local-branches" {
|
||||
if err := gui.handleBranchSelect(gui.g, branchesView); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.handleBranchSelect(gui.g, branchesView); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -29,9 +29,7 @@ func (gui *Gui) handleCommitFileSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
gui.getMainView().Title = "Patch"
|
||||
if gui.currentViewName() == "commitFiles" {
|
||||
gui.handleEscapeLineByLinePanel()
|
||||
}
|
||||
gui.State.Panels.LineByLine = nil
|
||||
|
||||
commitFile := gui.getSelectedCommitFile(g)
|
||||
if commitFile == nil {
|
||||
@@ -45,15 +43,11 @@ func (gui *Gui) handleCommitFileSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := gui.focusPoint(0, gui.State.Panels.CommitFiles.SelectedLine, len(gui.State.CommitFiles), v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.ShowCommitFileCmdStr(commitFile.Sha, commitFile.Name, false),
|
||||
)
|
||||
if err := gui.newCmdTask("main", cmd); err != nil {
|
||||
gui.Log.Error(err)
|
||||
commitText, err := gui.GitCommand.ShowCommitFile(commitFile.Sha, commitFile.Name, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return gui.renderString(g, "main", commitText)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSwitchToCommitsPanel(g *gocui.Gui, v *gocui.View) error {
|
||||
@@ -196,7 +190,9 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
|
||||
}
|
||||
}
|
||||
|
||||
gui.changeMainViewsContext("patch-building")
|
||||
if err := gui.changeMainViewsContext("patch-building"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.switchFocus(gui.g, gui.getCommitFilesView(), gui.getMainView()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -37,23 +37,13 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
|
||||
state := gui.State.Panels.Commits
|
||||
if state.SelectedLine > 20 && state.LimitCommits {
|
||||
state.LimitCommits = false
|
||||
go func() {
|
||||
if err := gui.refreshCommitsWithLimit(); err != nil {
|
||||
_ = gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
gui.getMainView().Title = "Patch"
|
||||
gui.getSecondaryView().Title = "Custom Patch"
|
||||
gui.handleEscapeLineByLinePanel()
|
||||
gui.State.Panels.LineByLine = nil
|
||||
|
||||
commit := gui.getSelectedCommit(g)
|
||||
if commit == nil {
|
||||
return gui.newStringTask("main", gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||
}
|
||||
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits), v); err != nil {
|
||||
@@ -65,32 +55,41 @@ func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.ShowCmdStr(commit.Sha),
|
||||
)
|
||||
if err := gui.newCmdTask("main", cmd); err != nil {
|
||||
gui.Log.Error(err)
|
||||
commitText, err := gui.GitCommand.Show(commit.Sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return gui.renderString(g, "main", commitText)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
||||
g.Update(func(*gocui.Gui) error {
|
||||
// I think this is here for the sake of some kind of rebasing thing
|
||||
gui.refreshStatus(g)
|
||||
builder, err := commands.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr, gui.State.CherryPickedCommits, gui.State.DiffEntries)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commits, err := builder.GetCommits()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gui.State.Commits = commits
|
||||
|
||||
if err := gui.refreshCommitsWithLimit(); err != nil {
|
||||
gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits))
|
||||
|
||||
isFocused := gui.g.CurrentView().Name() == "commits"
|
||||
list, err := utils.RenderList(gui.State.Commits, isFocused)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// doing this async because it shouldn't hold anything up
|
||||
go func() {
|
||||
if err := gui.refreshReflogCommits(); err != nil {
|
||||
_ = gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
}()
|
||||
v := gui.getCommitsView()
|
||||
v.Clear()
|
||||
fmt.Fprint(v, list)
|
||||
|
||||
gui.refreshStatus(g)
|
||||
if g.CurrentView() == v {
|
||||
gui.handleCommitSelect(g, v)
|
||||
}
|
||||
if g.CurrentView() == gui.getCommitFilesView() || (g.CurrentView() == gui.getMainView() || gui.State.MainContext == "patch-building") {
|
||||
return gui.refreshCommitFilesView()
|
||||
}
|
||||
@@ -99,27 +98,6 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshCommitsWithLimit() error {
|
||||
builder, err := commands.NewCommitListBuilder(gui.Log, gui.GitCommand, gui.OSCommand, gui.Tr, gui.State.CherryPickedCommits, gui.State.DiffEntries)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commits, err := builder.GetCommits(gui.State.Panels.Commits.LimitCommits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gui.State.Commits = commits
|
||||
|
||||
if gui.getCommitsView().Context == "branch-commits" {
|
||||
if err := gui.renderBranchCommitsWithSelection(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// specific functions
|
||||
|
||||
func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error {
|
||||
@@ -466,7 +444,7 @@ func (gui *Gui) handleToggleDiffCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
// get selected commit
|
||||
commit := gui.getSelectedCommit(g)
|
||||
if commit == nil {
|
||||
return gui.newStringTask("main", gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||
}
|
||||
|
||||
// if already selected commit delete
|
||||
@@ -489,7 +467,7 @@ func (gui *Gui) handleToggleDiffCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
|
||||
return gui.newStringTask("main", commitText)
|
||||
return gui.renderString(g, "main", commitText)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -637,67 +615,10 @@ func (gui *Gui) handleCreateLightweightTag(commitSha string) error {
|
||||
func (gui *Gui) handleCheckoutCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
commit := gui.getSelectedCommit(g)
|
||||
if commit == nil {
|
||||
return nil
|
||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||
}
|
||||
|
||||
return gui.createConfirmationPanel(g, gui.getCommitsView(), true, gui.Tr.SLocalize("checkoutCommit"), gui.Tr.SLocalize("SureCheckoutThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.handleCheckoutRef(commit.Sha)
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (gui *Gui) renderBranchCommitsWithSelection() error {
|
||||
commitsView := gui.getCommitsView()
|
||||
|
||||
gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits))
|
||||
if err := gui.renderListPanel(commitsView, gui.State.Commits); err != nil {
|
||||
return err
|
||||
}
|
||||
if gui.g.CurrentView() == commitsView && commitsView.Context == "branch-commits" {
|
||||
if err := gui.handleCommitSelect(gui.g, commitsView); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) onCommitsTabClick(tabIndex int) error {
|
||||
contexts := []string{"branch-commits", "reflog-commits"}
|
||||
commitsView := gui.getCommitsView()
|
||||
commitsView.TabIndex = tabIndex
|
||||
|
||||
return gui.switchCommitsPanelContext(contexts[tabIndex])
|
||||
}
|
||||
|
||||
func (gui *Gui) switchCommitsPanelContext(context string) error {
|
||||
commitsView := gui.getCommitsView()
|
||||
commitsView.Context = context
|
||||
|
||||
contextTabIndexMap := map[string]int{
|
||||
"branch-commits": 0,
|
||||
"reflog-commits": 1,
|
||||
}
|
||||
|
||||
commitsView.TabIndex = contextTabIndexMap[context]
|
||||
|
||||
switch context {
|
||||
case "branch-commits":
|
||||
return gui.renderBranchCommitsWithSelection()
|
||||
case "reflog-commits":
|
||||
return gui.renderReflogCommitsWithSelection()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleNextCommitsTab(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.onCommitsTabClick(
|
||||
utils.ModuloWithWrap(v.TabIndex+1, len(v.Tabs)),
|
||||
)
|
||||
}
|
||||
|
||||
func (gui *Gui) handlePrevCommitsTab(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.onCommitsTabClick(
|
||||
utils.ModuloWithWrap(v.TabIndex-1, len(v.Tabs)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -75,9 +75,6 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
|
||||
return nil, err
|
||||
}
|
||||
confirmationView.HasLoader = hasLoader
|
||||
if hasLoader {
|
||||
gui.g.StartTicking()
|
||||
}
|
||||
confirmationView.Title = title
|
||||
confirmationView.Wrap = true
|
||||
confirmationView.FgColor = theme.GocuiDefaultTextColor
|
||||
|
||||
@@ -4,9 +4,9 @@ package gui
|
||||
// which currently just means a context that affects both the main and secondary views
|
||||
// other views can have their context changed directly but this function helps
|
||||
// keep the main and secondary views in sync
|
||||
func (gui *Gui) changeMainViewsContext(context string) {
|
||||
func (gui *Gui) changeMainViewsContext(context string) error {
|
||||
if gui.State.MainContext == context {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
switch context {
|
||||
@@ -16,5 +16,5 @@ func (gui *Gui) changeMainViewsContext(context string) {
|
||||
}
|
||||
|
||||
gui.State.MainContext = context
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,20 +19,13 @@ type fileWatcher struct {
|
||||
Watcher *fsnotify.Watcher
|
||||
WatchedFilenames []string
|
||||
Log *logrus.Entry
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
func NewFileWatcher(log *logrus.Entry) *fileWatcher {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
log.Error(err)
|
||||
return &fileWatcher{
|
||||
Disabled: true,
|
||||
}
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return &fileWatcher{
|
||||
Disabled: true,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return &fileWatcher{
|
||||
@@ -73,10 +66,6 @@ func (w *fileWatcher) watchFilename(filename string) {
|
||||
}
|
||||
|
||||
func (w *fileWatcher) addFilesToFileWatcher(files []*commands.File) error {
|
||||
if w.Disabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -116,7 +105,7 @@ func min(a int, b int) int {
|
||||
// TODO: consider watching the whole directory recursively (could be more expensive)
|
||||
func (gui *Gui) watchFilesForChanges() {
|
||||
gui.fileWatcher = NewFileWatcher(gui.Log)
|
||||
if gui.fileWatcher.Disabled {
|
||||
if gui.fileWatcher == nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
|
||||
@@ -32,9 +32,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
||||
if err != gui.Errors.ErrNoFiles {
|
||||
return err
|
||||
}
|
||||
gui.State.SplitMainPanel = false
|
||||
gui.getMainView().Title = ""
|
||||
return gui.newStringTask("main", gui.Tr.SLocalize("NoChangedFiles"))
|
||||
return gui.renderString(gui.g, "main", gui.Tr.SLocalize("NoChangedFiles"))
|
||||
}
|
||||
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Files.SelectedLine, len(gui.State.Files), gui.getFilesView()); err != nil {
|
||||
@@ -47,40 +45,37 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
||||
return gui.refreshMergePanel()
|
||||
}
|
||||
|
||||
if !alreadySelected {
|
||||
if err := gui.resetOrigin(gui.getMainView()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.resetOrigin(gui.getSecondaryView()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
content := gui.GitCommand.Diff(file, false, false)
|
||||
contentCached := gui.GitCommand.Diff(file, false, true)
|
||||
leftContent := content
|
||||
if file.HasStagedChanges && file.HasUnstagedChanges {
|
||||
gui.State.SplitMainPanel = true
|
||||
gui.getMainView().Title = gui.Tr.SLocalize("UnstagedChanges")
|
||||
gui.getSecondaryView().Title = gui.Tr.SLocalize("StagedChanges")
|
||||
cmdStr := gui.GitCommand.DiffCmdStr(file, false, true)
|
||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||
if err := gui.newCmdTask("secondary", cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
gui.State.SplitMainPanel = false
|
||||
if file.HasUnstagedChanges {
|
||||
leftContent = content
|
||||
gui.getMainView().Title = gui.Tr.SLocalize("UnstagedChanges")
|
||||
} else {
|
||||
leftContent = contentCached
|
||||
gui.getMainView().Title = gui.Tr.SLocalize("StagedChanges")
|
||||
}
|
||||
}
|
||||
|
||||
cmdStr := gui.GitCommand.DiffCmdStr(file, false, !file.HasUnstagedChanges && file.HasStagedChanges)
|
||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||
if err := gui.newCmdTask("main", cmd); err != nil {
|
||||
if alreadySelected {
|
||||
gui.g.Update(func(*gocui.Gui) error {
|
||||
if err := gui.setViewContent(gui.g, gui.getSecondaryView(), contentCached); err != nil {
|
||||
return err
|
||||
}
|
||||
return gui.setViewContent(gui.g, gui.getMainView(), leftContent)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
if err := gui.renderString(gui.g, "secondary", contentCached); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return gui.renderString(gui.g, "main", leftContent)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshFiles() error {
|
||||
@@ -173,7 +168,9 @@ func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error
|
||||
if file.HasMergeConflicts {
|
||||
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("FileStagingRequirements"))
|
||||
}
|
||||
gui.changeMainViewsContext("staging")
|
||||
if err := gui.changeMainViewsContext("staging"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.switchFocus(gui.g, gui.getFilesView(), gui.getMainView()); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -372,15 +369,15 @@ func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
||||
if err != gui.Errors.ErrNoFiles {
|
||||
return "", err
|
||||
}
|
||||
return "", gui.newStringTask("main", gui.Tr.SLocalize("NoFilesDisplay"))
|
||||
return "", gui.renderString(g, "main", gui.Tr.SLocalize("NoFilesDisplay"))
|
||||
}
|
||||
if item.Type != "file" {
|
||||
return "", gui.newStringTask("main", gui.Tr.SLocalize("NotAFile"))
|
||||
return "", gui.renderString(g, "main", gui.Tr.SLocalize("NotAFile"))
|
||||
}
|
||||
cat, err := gui.GitCommand.CatFile(item.Name)
|
||||
if err != nil {
|
||||
gui.Log.Error(err)
|
||||
return "", gui.newStringTask("main", err.Error())
|
||||
return "", gui.renderString(g, "main", err.Error())
|
||||
}
|
||||
return cat, nil
|
||||
}
|
||||
@@ -393,17 +390,6 @@ func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
if pullables == "?" {
|
||||
// see if we have this branch in our config with an upstream
|
||||
conf, err := gui.GitCommand.Repo.Config()
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
for branchName, branch := range conf.Branches {
|
||||
if branchName == currentBranchName {
|
||||
return gui.pullFiles(v, fmt.Sprintf("%s %s", branch.Remote, branchName))
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin/"+currentBranchName, func(g *gocui.Gui, v *gocui.View) error {
|
||||
upstream := gui.trimmedContent(v)
|
||||
if err := gui.GitCommand.SetUpstreamBranch(upstream); err != nil {
|
||||
@@ -413,21 +399,21 @@ func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
return gui.createErrorPanel(gui.g, errorMessage)
|
||||
}
|
||||
return gui.pullFiles(v, "")
|
||||
return gui.pullFiles(v)
|
||||
})
|
||||
}
|
||||
|
||||
return gui.pullFiles(v, "")
|
||||
return gui.pullFiles(v)
|
||||
}
|
||||
|
||||
func (gui *Gui) pullFiles(v *gocui.View, args string) error {
|
||||
func (gui *Gui) pullFiles(v *gocui.View) error {
|
||||
if err := gui.createLoaderPanel(gui.g, v, gui.Tr.SLocalize("PullWait")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
unamePassOpend := false
|
||||
err := gui.GitCommand.Pull(args, func(passOrUname string) string {
|
||||
err := gui.GitCommand.Pull(func(passOrUname string) string {
|
||||
unamePassOpend = true
|
||||
return gui.waitForPassUname(gui.g, v, passOrUname)
|
||||
})
|
||||
@@ -437,14 +423,14 @@ func (gui *Gui) pullFiles(v *gocui.View, args string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool, upstream string, args string) error {
|
||||
func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool, upstream string) error {
|
||||
if err := gui.createLoaderPanel(gui.g, v, gui.Tr.SLocalize("PushWait")); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
unamePassOpend := false
|
||||
branchName := gui.getCheckedOutBranch().Name
|
||||
err := gui.GitCommand.Push(branchName, force, upstream, args, func(passOrUname string) string {
|
||||
err := gui.GitCommand.Push(branchName, force, upstream, func(passOrUname string) string {
|
||||
unamePassOpend = true
|
||||
return gui.waitForPassUname(g, v, passOrUname)
|
||||
})
|
||||
@@ -462,25 +448,14 @@ func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
if pullables == "?" {
|
||||
// see if we have this branch in our config with an upstream
|
||||
conf, err := gui.GitCommand.Repo.Config()
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
for branchName, branch := range conf.Branches {
|
||||
if branchName == currentBranchName {
|
||||
return gui.pushWithForceFlag(g, v, false, "", fmt.Sprintf("%s %s", branch.Remote, branchName))
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin "+currentBranchName, func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.pushWithForceFlag(g, v, false, gui.trimmedContent(v), "")
|
||||
return gui.pushWithForceFlag(g, v, false, gui.trimmedContent(v))
|
||||
})
|
||||
} else if pullables == "0" {
|
||||
return gui.pushWithForceFlag(g, v, false, "", "")
|
||||
return gui.pushWithForceFlag(g, v, false, "")
|
||||
}
|
||||
return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("ForcePush"), gui.Tr.SLocalize("ForcePushPrompt"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.pushWithForceFlag(g, v, true, "", "")
|
||||
return gui.createConfirmationPanel(g, nil, true, gui.Tr.SLocalize("ForcePush"), gui.Tr.SLocalize("ForcePushPrompt"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.pushWithForceFlag(g, v, true, "")
|
||||
}, nil)
|
||||
}
|
||||
|
||||
@@ -495,7 +470,9 @@ func (gui *Gui) handleSwitchToMerge(g *gocui.Gui, v *gocui.View) error {
|
||||
if !file.HasInlineMergeConflicts {
|
||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("FileNoMergeCons"))
|
||||
}
|
||||
gui.changeMainViewsContext("merging")
|
||||
if err := gui.changeMainViewsContext("merging"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.switchFocus(g, v, gui.getMainView()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
162
pkg/gui/gui.go
162
pkg/gui/gui.go
@@ -25,7 +25,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/tasks"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -69,22 +68,20 @@ type Teml i18n.Teml
|
||||
|
||||
// Gui wraps the gocui Gui object which handles rendering and events
|
||||
type Gui struct {
|
||||
g *gocui.Gui
|
||||
Log *logrus.Entry
|
||||
GitCommand *commands.GitCommand
|
||||
OSCommand *commands.OSCommand
|
||||
SubProcess *exec.Cmd
|
||||
State guiState
|
||||
Config config.AppConfigurer
|
||||
Tr *i18n.Localizer
|
||||
Errors SentinelErrors
|
||||
Updater *updates.Updater
|
||||
statusManager *statusManager
|
||||
credentials credentials
|
||||
waitForIntro sync.WaitGroup
|
||||
fileWatcher *fileWatcher
|
||||
viewBufferManagerMap map[string]*tasks.ViewBufferManager
|
||||
stopChan chan struct{}
|
||||
g *gocui.Gui
|
||||
Log *logrus.Entry
|
||||
GitCommand *commands.GitCommand
|
||||
OSCommand *commands.OSCommand
|
||||
SubProcess *exec.Cmd
|
||||
State guiState
|
||||
Config config.AppConfigurer
|
||||
Tr *i18n.Localizer
|
||||
Errors SentinelErrors
|
||||
Updater *updates.Updater
|
||||
statusManager *statusManager
|
||||
credentials credentials
|
||||
waitForIntro sync.WaitGroup
|
||||
fileWatcher *fileWatcher
|
||||
}
|
||||
|
||||
// for now the staging panel state, unlike the other panel states, is going to be
|
||||
@@ -131,11 +128,6 @@ type tagsPanelState struct {
|
||||
type commitPanelState struct {
|
||||
SelectedLine int
|
||||
SpecificDiffMode bool
|
||||
LimitCommits bool
|
||||
}
|
||||
|
||||
type reflogCommitPanelState struct {
|
||||
SelectedLine int
|
||||
}
|
||||
|
||||
type stashPanelState struct {
|
||||
@@ -163,7 +155,6 @@ type panelStates struct {
|
||||
RemoteBranches *remoteBranchesState
|
||||
Tags *tagsPanelState
|
||||
Commits *commitPanelState
|
||||
ReflogCommits *reflogCommitPanelState
|
||||
Stash *stashPanelState
|
||||
Menu *menuPanelState
|
||||
LineByLine *lineByLinePanelState
|
||||
@@ -178,7 +169,6 @@ type guiState struct {
|
||||
Commits []*commands.Commit
|
||||
StashEntries []*commands.StashEntry
|
||||
CommitFiles []*commands.CommitFile
|
||||
ReflogCommits []*commands.Commit
|
||||
DiffEntries []*commands.Commit
|
||||
Remotes []*commands.Remote
|
||||
RemoteBranches []*commands.RemoteBranch
|
||||
@@ -216,8 +206,7 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
|
||||
Remotes: &remotePanelState{SelectedLine: 0},
|
||||
RemoteBranches: &remoteBranchesState{SelectedLine: -1},
|
||||
Tags: &tagsPanelState{SelectedLine: -1},
|
||||
Commits: &commitPanelState{SelectedLine: -1, LimitCommits: true},
|
||||
ReflogCommits: &reflogCommitPanelState{SelectedLine: 0}, // TODO: might need to make -1
|
||||
Commits: &commitPanelState{SelectedLine: -1},
|
||||
CommitFiles: &commitFilesPanelState{SelectedLine: -1},
|
||||
Stash: &stashPanelState{SelectedLine: -1},
|
||||
Menu: &menuPanelState{SelectedLine: 0},
|
||||
@@ -232,15 +221,14 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
|
||||
}
|
||||
|
||||
gui := &Gui{
|
||||
Log: log,
|
||||
GitCommand: gitCommand,
|
||||
OSCommand: oSCommand,
|
||||
State: initialState,
|
||||
Config: config,
|
||||
Tr: tr,
|
||||
Updater: updater,
|
||||
statusManager: &statusManager{},
|
||||
viewBufferManagerMap: map[string]*tasks.ViewBufferManager{},
|
||||
Log: log,
|
||||
GitCommand: gitCommand,
|
||||
OSCommand: oSCommand,
|
||||
State: initialState,
|
||||
Config: config,
|
||||
Tr: tr,
|
||||
Updater: updater,
|
||||
statusManager: &statusManager{},
|
||||
}
|
||||
|
||||
gui.watchFilesForChanges()
|
||||
@@ -265,14 +253,8 @@ func (gui *Gui) scrollDownView(viewName string) error {
|
||||
_, sy := mainView.Size()
|
||||
y += sy
|
||||
}
|
||||
scrollHeight := gui.Config.GetUserConfig().GetInt("gui.scrollHeight")
|
||||
if y < mainView.LinesHeight() {
|
||||
if err := mainView.SetOrigin(ox, oy+scrollHeight); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if manager, ok := gui.viewBufferManagerMap[viewName]; ok {
|
||||
manager.ReadLines(scrollHeight)
|
||||
if y < len(mainView.BufferLines()) {
|
||||
return mainView.SetOrigin(ox, oy+gui.Config.GetUserConfig().GetInt("gui.scrollHeight"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -349,7 +331,9 @@ func (gui *Gui) onFocusLost(v *gocui.View, newView *gocui.View) error {
|
||||
}
|
||||
case "main":
|
||||
// if we have lost focus to a first-class panel, we need to do some cleanup
|
||||
gui.changeMainViewsContext("normal")
|
||||
if err := gui.changeMainViewsContext("normal"); err != nil {
|
||||
return err
|
||||
}
|
||||
case "commitFiles":
|
||||
if gui.State.MainContext != "patch-building" {
|
||||
if _, err := gui.g.SetViewOnBottom(v.Name()); err != nil {
|
||||
@@ -459,22 +443,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
textColor := theme.GocuiDefaultTextColor
|
||||
leftSideWidth := width / 3
|
||||
panelSplitX := width - 1
|
||||
mainPanelRight := width - 1
|
||||
secondaryPanelLeft := width - 1
|
||||
secondaryPanelTop := 0
|
||||
mainPanelBottom := height - 2
|
||||
if gui.State.SplitMainPanel {
|
||||
if width < 220 {
|
||||
mainPanelBottom = height/2 - 1
|
||||
secondaryPanelTop = mainPanelBottom + 1
|
||||
secondaryPanelLeft = leftSideWidth + 1
|
||||
} else {
|
||||
units := 5
|
||||
leftSideWidth = width / units
|
||||
panelSplitX = (1 + ((units - 1) / 2)) * width / units
|
||||
mainPanelRight = panelSplitX
|
||||
secondaryPanelLeft = panelSplitX + 1
|
||||
}
|
||||
units := 7
|
||||
leftSideWidth = width / units
|
||||
panelSplitX = (1 + ((units - 1) / 2)) * width / units
|
||||
}
|
||||
|
||||
main := "main"
|
||||
@@ -485,22 +457,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
secondary = "main"
|
||||
}
|
||||
|
||||
// reading more lines into main view buffers upon resize
|
||||
prevMainView, err := gui.g.View("main")
|
||||
if err == nil {
|
||||
_, prevMainHeight := prevMainView.Size()
|
||||
heightDiff := mainPanelBottom - prevMainHeight
|
||||
if heightDiff > 0 {
|
||||
if manager, ok := gui.viewBufferManagerMap["main"]; ok {
|
||||
manager.ReadLines(heightDiff)
|
||||
}
|
||||
if manager, ok := gui.viewBufferManagerMap["secondary"]; ok {
|
||||
manager.ReadLines(heightDiff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v, err := g.SetView(main, leftSideWidth+panelSpacing, 0, mainPanelRight, mainPanelBottom, gocui.LEFT)
|
||||
v, err := g.SetView(main, leftSideWidth+panelSpacing, 0, panelSplitX, height-2, gocui.LEFT)
|
||||
if err != nil {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
@@ -514,7 +471,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
if !gui.State.SplitMainPanel {
|
||||
hiddenViewOffset = 9999
|
||||
}
|
||||
secondaryView, err := g.SetView(secondary, secondaryPanelLeft+hiddenViewOffset, hiddenViewOffset+secondaryPanelTop, width-1+hiddenViewOffset, height-2+hiddenViewOffset, gocui.LEFT)
|
||||
secondaryView, err := g.SetView(secondary, panelSplitX+1+hiddenViewOffset, hiddenViewOffset, width-1+hiddenViewOffset, height-2+hiddenViewOffset, gocui.LEFT)
|
||||
if err != nil {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
@@ -566,7 +523,6 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
commitsView.Title = gui.Tr.SLocalize("CommitsTitle")
|
||||
commitsView.Tabs = []string{"Commits", "Reflog"}
|
||||
commitsView.FgColor = textColor
|
||||
}
|
||||
|
||||
@@ -590,7 +546,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
|
||||
if gui.getCommitMessageView() == nil {
|
||||
// doesn't matter where this view starts because it will be hidden
|
||||
if commitMessageView, err := g.SetView("commitMessage", hiddenViewOffset, hiddenViewOffset, hiddenViewOffset+10, hiddenViewOffset+10, 0); err != nil {
|
||||
if commitMessageView, err := g.SetView("commitMessage", width, height, width*2, height*2, 0); err != nil {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
@@ -604,7 +560,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
|
||||
if check, _ := g.View("credentials"); check == nil {
|
||||
// doesn't matter where this view starts because it will be hidden
|
||||
if credentialsView, err := g.SetView("credentials", hiddenViewOffset, hiddenViewOffset, hiddenViewOffset+10, hiddenViewOffset+10, 0); err != nil {
|
||||
if credentialsView, err := g.SetView("credentials", width, height, width*2, height*2, 0); err != nil {
|
||||
if err.Error() != "unknown view" {
|
||||
return err
|
||||
}
|
||||
@@ -669,8 +625,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
{view: branchesView, context: "local-branches", selectedLine: gui.State.Panels.Branches.SelectedLine, lineCount: len(gui.State.Branches)},
|
||||
{view: branchesView, context: "remotes", selectedLine: gui.State.Panels.Remotes.SelectedLine, lineCount: len(gui.State.Remotes)},
|
||||
{view: branchesView, context: "remote-branches", selectedLine: gui.State.Panels.RemoteBranches.SelectedLine, lineCount: len(gui.State.Remotes)},
|
||||
{view: commitsView, context: "branch-commits", selectedLine: gui.State.Panels.Commits.SelectedLine, lineCount: len(gui.State.Commits)},
|
||||
{view: commitsView, context: "reflog-commits", selectedLine: gui.State.Panels.ReflogCommits.SelectedLine, lineCount: len(gui.State.ReflogCommits)},
|
||||
{view: commitsView, context: "", selectedLine: gui.State.Panels.Commits.SelectedLine, lineCount: len(gui.State.Commits)},
|
||||
{view: stashView, context: "", selectedLine: gui.State.Panels.Stash.SelectedLine, lineCount: len(gui.State.StashEntries)},
|
||||
}
|
||||
|
||||
@@ -697,10 +652,11 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) onInitialViewsCreation() error {
|
||||
gui.changeMainViewsContext("normal")
|
||||
if err := gui.changeMainViewsContext("normal"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.getBranchesView().Context = "local-branches"
|
||||
gui.getCommitsView().Context = "branch-commits"
|
||||
|
||||
return gui.loadNewRepo()
|
||||
}
|
||||
@@ -776,6 +732,14 @@ func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canAskForCredentials bool) (u
|
||||
return unamePassOpend, err
|
||||
}
|
||||
|
||||
func (gui *Gui) renderAppStatus() error {
|
||||
appStatus := gui.statusManager.getStatusString()
|
||||
if appStatus != "" {
|
||||
return gui.renderString(gui.g, "appStatus", appStatus)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) renderGlobalOptions() error {
|
||||
return gui.renderOptionsMap(map[string]string{
|
||||
fmt.Sprintf("%s/%s", gui.getKeyDisplay("universal.scrollUpMain"), gui.getKeyDisplay("universal.scrollDownMain")): gui.Tr.SLocalize("scroll"),
|
||||
@@ -786,17 +750,10 @@ func (gui *Gui) renderGlobalOptions() error {
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) goEvery(interval time.Duration, stop chan struct{}, function func() error) {
|
||||
func (gui *Gui) goEvery(interval time.Duration, function func() error) {
|
||||
go func() {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
_ = function()
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
for range time.Tick(interval) {
|
||||
_ = function()
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -811,7 +768,7 @@ func (gui *Gui) startBackgroundFetch() {
|
||||
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
||||
_ = gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), true, gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil)
|
||||
} else {
|
||||
gui.goEvery(time.Second*60, gui.stopChan, func() error {
|
||||
gui.goEvery(time.Second*60, func() error {
|
||||
_, err := gui.fetch(gui.g, gui.g.CurrentView(), false)
|
||||
return err
|
||||
})
|
||||
@@ -825,7 +782,6 @@ func (gui *Gui) Run() error {
|
||||
return err
|
||||
}
|
||||
defer g.Close()
|
||||
gui.stopChan = make(chan struct{})
|
||||
|
||||
g.ASCII = runtime.GOOS == "windows" && runewidth.IsEastAsian()
|
||||
|
||||
@@ -855,7 +811,8 @@ func (gui *Gui) Run() error {
|
||||
go gui.startBackgroundFetch()
|
||||
}
|
||||
|
||||
gui.goEvery(time.Second*10, gui.stopChan, gui.refreshFiles)
|
||||
gui.goEvery(time.Second*10, gui.refreshFiles)
|
||||
gui.goEvery(time.Millisecond*50, gui.renderAppStatus)
|
||||
|
||||
g.SetManager(gocui.ManagerFunc(gui.layout), gocui.ManagerFunc(gui.getFocusLayout()))
|
||||
|
||||
@@ -875,17 +832,6 @@ func (gui *Gui) Run() error {
|
||||
func (gui *Gui) RunWithSubprocesses() error {
|
||||
for {
|
||||
if err := gui.Run(); err != nil {
|
||||
for _, manager := range gui.viewBufferManagerMap {
|
||||
manager.Close()
|
||||
}
|
||||
gui.viewBufferManagerMap = map[string]*tasks.ViewBufferManager{}
|
||||
|
||||
if !gui.fileWatcher.Disabled {
|
||||
gui.fileWatcher.Watcher.Close()
|
||||
}
|
||||
|
||||
close(gui.stopChan)
|
||||
|
||||
if err == gocui.ErrQuit {
|
||||
if !gui.State.RetainOriginalDir {
|
||||
if err := gui.recordCurrentDirectory(); err != nil {
|
||||
@@ -893,6 +839,8 @@ func (gui *Gui) RunWithSubprocesses() error {
|
||||
}
|
||||
}
|
||||
|
||||
gui.fileWatcher.Watcher.Close()
|
||||
|
||||
break
|
||||
} else if err == gui.Errors.ErrSwitchRepo {
|
||||
continue
|
||||
|
||||
@@ -571,13 +571,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "branches",
|
||||
Key: gui.getKey("universal.nextTab"),
|
||||
Key: gui.getKey("universal.nextBranchTab"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleNextBranchesTab,
|
||||
},
|
||||
{
|
||||
ViewName: "branches",
|
||||
Key: gui.getKey("universal.prevTab"),
|
||||
Key: gui.getKey("universal.prevBranchTab"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handlePrevBranchesTab,
|
||||
},
|
||||
@@ -597,21 +597,8 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
Handler: gui.handleFetchRemote,
|
||||
Description: gui.Tr.SLocalize("fetchRemote"),
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Key: gui.getKey("universal.nextTab"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleNextCommitsTab,
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Key: gui.getKey("universal.prevTab"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handlePrevCommitsTab,
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.squashDown"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitSquashDown,
|
||||
@@ -619,7 +606,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.renameCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleRenameCommit,
|
||||
@@ -627,7 +613,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.renameCommitWithEditor"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleRenameCommitEditor,
|
||||
@@ -635,7 +620,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.viewResetOptions"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCreateCommitResetMenu,
|
||||
@@ -643,7 +627,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.markCommitAsFixup"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitFixup,
|
||||
@@ -651,7 +634,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.createFixupCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCreateFixupCommit,
|
||||
@@ -659,7 +641,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.squashAboveCommits"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleSquashAllAboveFixupCommits,
|
||||
@@ -667,7 +648,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("universal.remove"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitDelete,
|
||||
@@ -675,7 +655,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.moveDownCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitMoveDown,
|
||||
@@ -683,7 +662,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.moveUpCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitMoveUp,
|
||||
@@ -691,7 +669,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("universal.edit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitEdit,
|
||||
@@ -699,7 +676,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.amendToCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitAmendTo,
|
||||
@@ -707,7 +683,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.pickCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitPick,
|
||||
@@ -715,7 +690,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.revertCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitRevert,
|
||||
@@ -723,7 +697,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.cherryPickCopy"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCopyCommit,
|
||||
@@ -731,7 +704,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.cherryPickCopyRange"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCopyCommitRange,
|
||||
@@ -739,7 +711,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.pasteCommits"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.HandlePasteCommits,
|
||||
@@ -747,7 +718,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("universal.goInto"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleSwitchToCommitFilesPanel,
|
||||
@@ -755,7 +725,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.checkoutCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCheckoutCommit,
|
||||
@@ -763,7 +732,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.toggleDiffCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleToggleDiffCommit,
|
||||
@@ -771,28 +739,11 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"branch-commits"},
|
||||
Key: gui.getKey("commits.tagCommit"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleTagCommit,
|
||||
Description: gui.Tr.SLocalize("tagCommit"),
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"reflog-commits"},
|
||||
Key: gui.getKey("universal.select"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCheckoutReflogCommit,
|
||||
Description: gui.Tr.SLocalize("checkoutCommit"),
|
||||
},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Contexts: []string{"reflog-commits"},
|
||||
Key: gui.getKey("commits.viewResetOptions"),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCreateReflogResetMenu,
|
||||
Description: gui.Tr.SLocalize("viewResetOptions"),
|
||||
},
|
||||
{
|
||||
ViewName: "stash",
|
||||
Key: gui.getKey("universal.select"),
|
||||
@@ -1384,15 +1335,8 @@ func (gui *Gui) keybindings(g *gocui.Gui) error {
|
||||
}
|
||||
}
|
||||
|
||||
tabClickBindings := map[string]func(int) error{
|
||||
"branches": gui.onBranchesTabClick,
|
||||
"commits": gui.onCommitsTabClick,
|
||||
}
|
||||
|
||||
for viewName, binding := range tabClickBindings {
|
||||
if err := g.SetTabClickBinding(viewName, binding); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetTabClickBinding("branches", gui.onBranchesTabClick); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -314,8 +314,3 @@ func (gui *Gui) handleToggleSelectHunk(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
return gui.focusSelection(state.SelectMode == HUNK)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEscapeLineByLinePanel() {
|
||||
gui.changeMainViewsContext("normal")
|
||||
gui.State.Panels.LineByLine = nil
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ func (lv *listView) handleLineChange(change int) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
view, err := lv.gui.g.View(lv.viewName)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -57,14 +56,7 @@ func (lv *listView) handleClick(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
*selectedLineIdxPtr = newSelectedLineIdx
|
||||
|
||||
if lv.rendersToMainView {
|
||||
if err := lv.gui.resetOrigin(lv.gui.getMainView()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
prevViewName := lv.gui.currentViewName()
|
||||
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lv.viewName && lv.handleClickSelectedItem != nil {
|
||||
if prevSelectedLineIdx == newSelectedLineIdx && lv.gui.currentViewName() == lv.viewName && lv.handleClickSelectedItem != nil {
|
||||
return lv.handleClickSelectedItem(lv.gui.g, v)
|
||||
}
|
||||
return lv.handleItemSelect(lv.gui.g, v)
|
||||
@@ -134,10 +126,8 @@ func (gui *Gui) getListViews() []*listView {
|
||||
gui: gui,
|
||||
rendersToMainView: true,
|
||||
},
|
||||
|
||||
{
|
||||
viewName: "commits",
|
||||
context: "branch-commits",
|
||||
getItemsLength: func() int { return len(gui.State.Commits) },
|
||||
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Commits.SelectedLine },
|
||||
handleFocus: gui.handleCommitSelect,
|
||||
@@ -146,16 +136,6 @@ func (gui *Gui) getListViews() []*listView {
|
||||
gui: gui,
|
||||
rendersToMainView: true,
|
||||
},
|
||||
{
|
||||
viewName: "commits",
|
||||
context: "reflog-commits",
|
||||
getItemsLength: func() int { return len(gui.State.ReflogCommits) },
|
||||
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.ReflogCommits.SelectedLine },
|
||||
handleFocus: gui.handleReflogCommitSelect,
|
||||
handleItemSelect: gui.handleReflogCommitSelect,
|
||||
gui: gui,
|
||||
rendersToMainView: true,
|
||||
},
|
||||
{
|
||||
viewName: "stash",
|
||||
getItemsLength: func() int { return len(gui.State.StashEntries) },
|
||||
|
||||
@@ -212,17 +212,16 @@ func (gui *Gui) refreshMergePanel() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mainView := gui.getMainView()
|
||||
mainView.Wrap = false
|
||||
if err := gui.setViewContent(gui.g, mainView, content); err != nil {
|
||||
if err := gui.renderString(gui.g, "main", content); err != nil {
|
||||
return err
|
||||
}
|
||||
gui.Log.Warn("scrolling to conflict")
|
||||
if err := gui.scrollToConflict(gui.g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mainView := gui.getMainView()
|
||||
mainView.Wrap = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,8 @@ func (gui *Gui) handleRemoveSelectionFromPatch(g *gocui.Gui, v *gocui.View) erro
|
||||
}
|
||||
|
||||
func (gui *Gui) handleEscapePatchBuildingPanel(g *gocui.Gui, v *gocui.View) error {
|
||||
gui.handleEscapeLineByLinePanel()
|
||||
gui.State.Panels.LineByLine = nil
|
||||
gui.changeMainViewsContext("normal")
|
||||
|
||||
if gui.GitCommand.PatchManager.IsEmpty() {
|
||||
gui.GitCommand.PatchManager.Reset()
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
|
||||
func (gui *Gui) getSelectedReflogCommit() *commands.Commit {
|
||||
selectedLine := gui.State.Panels.ReflogCommits.SelectedLine
|
||||
if selectedLine == -1 || len(gui.State.ReflogCommits) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.State.ReflogCommits[selectedLine]
|
||||
}
|
||||
|
||||
func (gui *Gui) handleReflogCommitSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
if gui.popupPanelFocused() {
|
||||
return nil
|
||||
}
|
||||
|
||||
gui.State.SplitMainPanel = false
|
||||
|
||||
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.getMainView().Title = "Reflog Entry"
|
||||
|
||||
commit := gui.getSelectedReflogCommit()
|
||||
if commit == nil {
|
||||
return gui.newStringTask("main", "No reflog history")
|
||||
}
|
||||
if err := gui.focusPoint(0, gui.State.Panels.ReflogCommits.SelectedLine, len(gui.State.ReflogCommits), v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.ShowCmdStr(commit.Sha),
|
||||
)
|
||||
if err := gui.newCmdTask("main", cmd); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshReflogCommits() error {
|
||||
commits, err := gui.GitCommand.GetReflogCommits()
|
||||
if err != nil {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
|
||||
gui.State.ReflogCommits = commits
|
||||
|
||||
if gui.getCommitsView().Context == "reflog-commits" {
|
||||
return gui.renderReflogCommitsWithSelection()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) renderReflogCommitsWithSelection() error {
|
||||
commitsView := gui.getCommitsView()
|
||||
|
||||
gui.refreshSelectedLine(&gui.State.Panels.ReflogCommits.SelectedLine, len(gui.State.ReflogCommits))
|
||||
if err := gui.renderListPanel(commitsView, gui.State.ReflogCommits); err != nil {
|
||||
return err
|
||||
}
|
||||
if gui.g.CurrentView() == commitsView && commitsView.Context == "reflog-commits" {
|
||||
if err := gui.handleReflogCommitSelect(gui.g, commitsView); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCheckoutReflogCommit(g *gocui.Gui, v *gocui.View) error {
|
||||
commit := gui.getSelectedReflogCommit()
|
||||
if commit == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := gui.createConfirmationPanel(g, gui.getCommitsView(), true, gui.Tr.SLocalize("checkoutCommit"), gui.Tr.SLocalize("SureCheckoutThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.handleCheckoutRef(commit.Sha)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.State.Panels.ReflogCommits.SelectedLine = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
)
|
||||
|
||||
type reflogResetOption struct {
|
||||
handler func() error
|
||||
description string
|
||||
command string
|
||||
}
|
||||
|
||||
// GetDisplayStrings is a function.
|
||||
func (r *reflogResetOption) GetDisplayStrings(isFocused bool) []string {
|
||||
return []string{r.description, color.New(color.FgRed).Sprint(r.command)}
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreateReflogResetMenu(g *gocui.Gui, v *gocui.View) error {
|
||||
commit := gui.getSelectedReflogCommit()
|
||||
|
||||
resetFunction := func(reset func(string) error) func() error {
|
||||
return func() error {
|
||||
if err := reset(commit.Sha); err != nil {
|
||||
return gui.createErrorPanel(gui.g, err.Error())
|
||||
}
|
||||
|
||||
gui.State.Panels.ReflogCommits.SelectedLine = 0
|
||||
|
||||
return gui.refreshSidePanels(gui.g)
|
||||
}
|
||||
}
|
||||
|
||||
options := []*reflogResetOption{
|
||||
{
|
||||
description: gui.Tr.SLocalize("hardReset"),
|
||||
command: fmt.Sprintf("reset --hard %s", commit.Sha),
|
||||
handler: resetFunction(gui.GitCommand.ResetHard),
|
||||
},
|
||||
{
|
||||
description: gui.Tr.SLocalize("softReset"),
|
||||
command: fmt.Sprintf("reset --soft %s", commit.Sha),
|
||||
handler: resetFunction(gui.GitCommand.ResetSoft),
|
||||
},
|
||||
{
|
||||
description: gui.Tr.SLocalize("cancel"),
|
||||
handler: func() error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
handleMenuPress := func(index int) error {
|
||||
return options[index].handler()
|
||||
}
|
||||
|
||||
return gui.createMenu("", options, len(options), handleMenuPress)
|
||||
}
|
||||
@@ -2,9 +2,12 @@ package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// list panel functions
|
||||
@@ -34,7 +37,7 @@ func (gui *Gui) handleRemoteBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
remote := gui.getSelectedRemote()
|
||||
remoteBranch := gui.getSelectedRemoteBranch()
|
||||
if remoteBranch == nil {
|
||||
return gui.newStringTask("main", "No branches for this remote")
|
||||
return gui.renderString(g, "main", "No branches for this remote")
|
||||
}
|
||||
|
||||
gui.focusPoint(0, gui.State.Panels.Menu.SelectedLine, gui.State.MenuItemCount, v)
|
||||
@@ -42,14 +45,13 @@ func (gui *Gui) handleRemoteBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
|
||||
branchName := fmt.Sprintf("%s/%s", remote.Name, remoteBranch.Name)
|
||||
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.GetBranchGraphCmdStr(branchName),
|
||||
)
|
||||
if err := gui.newCmdTask("main", cmd); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
go func() {
|
||||
graph, err := gui.GitCommand.GetBranchGraph(fmt.Sprintf("%s/%s", remote.Name, remoteBranch.Name))
|
||||
if err != nil && strings.HasPrefix(graph, "fatal: ambiguous argument") {
|
||||
graph = gui.Tr.SLocalize("NoTrackingThisBranch")
|
||||
}
|
||||
_ = gui.renderString(g, "main", fmt.Sprintf("%s/%s\n\n%s", utils.ColoredString(remote.Name, color.FgRed), utils.ColoredString(remoteBranch.Name, color.FgGreen), graph))
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -65,10 +67,8 @@ func (gui *Gui) renderRemoteBranchesWithSelection() error {
|
||||
if err := gui.renderListPanel(branchesView, gui.State.RemoteBranches); err != nil {
|
||||
return err
|
||||
}
|
||||
if gui.g.CurrentView() == branchesView && branchesView.Context == "remote-branches" {
|
||||
if err := gui.handleRemoteBranchSelect(gui.g, branchesView); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.handleRemoteBranchSelect(gui.g, branchesView); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -36,13 +36,13 @@ func (gui *Gui) handleRemoteSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
remote := gui.getSelectedRemote()
|
||||
if remote == nil {
|
||||
return gui.newStringTask("main", "No remotes")
|
||||
return gui.renderString(g, "main", "No remotes")
|
||||
}
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Remotes.SelectedLine, len(gui.State.Remotes), v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.newStringTask("main", fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n")))
|
||||
return gui.renderString(g, "main", fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n")))
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshRemotes() error {
|
||||
@@ -83,10 +83,8 @@ func (gui *Gui) renderRemotesWithSelection() error {
|
||||
if err := gui.renderListPanel(branchesView, gui.State.Remotes); err != nil {
|
||||
return err
|
||||
}
|
||||
if gui.g.CurrentView() == branchesView && branchesView.Context == "remotes" {
|
||||
if err := gui.handleRemoteSelect(gui.g, branchesView); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.handleRemoteSelect(gui.g, branchesView); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -12,13 +12,6 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
|
||||
|
||||
state := gui.State.Panels.LineByLine
|
||||
|
||||
// We need to force focus here because the confirmation panel for safely staging lines does not return focus automatically.
|
||||
// This is because if we tell it to return focus it will unconditionally return it to the main panel which may not be what we want
|
||||
// e.g. in the event that there's nothing left to stage.
|
||||
if err := gui.switchFocus(gui.g, nil, gui.getMainView()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := gui.getSelectedFile(gui.g)
|
||||
if err != nil {
|
||||
if err != gui.Errors.ErrNoFiles {
|
||||
@@ -94,36 +87,26 @@ func (gui *Gui) handleTogglePanel(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStagingEscape(g *gocui.Gui, v *gocui.View) error {
|
||||
gui.handleEscapeLineByLinePanel()
|
||||
gui.State.Panels.LineByLine = nil
|
||||
|
||||
return gui.switchFocus(gui.g, nil, gui.getFilesView())
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStageSelection(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.applySelectionWithPrompt(false)
|
||||
return gui.applySelection(false)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleResetSelection(g *gocui.Gui, v *gocui.View) error {
|
||||
return gui.applySelectionWithPrompt(true)
|
||||
}
|
||||
|
||||
func (gui *Gui) applySelectionWithPrompt(reverse bool) error {
|
||||
state := gui.State.Panels.LineByLine
|
||||
|
||||
if !reverse && state.SecondaryFocused {
|
||||
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("CantStageStaged"))
|
||||
} else if reverse && !state.SecondaryFocused && !gui.Config.GetUserConfig().GetBool("gui.skipUnstageLineWarning") {
|
||||
return gui.createConfirmationPanel(gui.g, gui.getMainView(), false, "unstage lines", "Are you sure you want to unstage these lines? It is irreversible.\nTo disable this dialogue set the config key of 'gui.skipUnstageLineWarning' to true", func(*gocui.Gui, *gocui.View) error {
|
||||
return gui.applySelection(reverse)
|
||||
}, nil)
|
||||
}
|
||||
|
||||
return gui.applySelection(reverse)
|
||||
return gui.applySelection(true)
|
||||
}
|
||||
|
||||
func (gui *Gui) applySelection(reverse bool) error {
|
||||
state := gui.State.Panels.LineByLine
|
||||
|
||||
if !reverse && state.SecondaryFocused {
|
||||
return gui.createErrorPanel(gui.g, gui.Tr.SLocalize("CantStageStaged"))
|
||||
}
|
||||
|
||||
file, err := gui.getSelectedFile(gui.g)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -34,19 +34,16 @@ func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
stashEntry := gui.getSelectedStashEntry(v)
|
||||
if stashEntry == nil {
|
||||
return gui.newStringTask("main", gui.Tr.SLocalize("NoStashEntries"))
|
||||
return gui.renderString(g, "main", gui.Tr.SLocalize("NoStashEntries"))
|
||||
}
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Stash.SelectedLine, len(gui.State.StashEntries), v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.ShowStashEntryCmdStr(stashEntry.Index),
|
||||
)
|
||||
if err := gui.newCmdTask("main", cmd); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
// doing this asynchronously cos it can take time
|
||||
diff, _ := gui.GitCommand.GetStashEntryDiff(stashEntry.Index)
|
||||
_ = gui.renderString(g, "main", diff)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
magenta.Sprint("Become a sponsor (github is matching all donations for 12 months): https://github.com/sponsors/jesseduffield"), // caffeine ain't free
|
||||
}, "\n\n")
|
||||
|
||||
return gui.newStringTask("main", dashboardString)
|
||||
return gui.renderString(g, "main", dashboardString)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
)
|
||||
@@ -31,18 +33,25 @@ func (gui *Gui) handleTagSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
|
||||
tag := gui.getSelectedTag()
|
||||
if tag == nil {
|
||||
return gui.newStringTask("main", "No tags")
|
||||
return gui.renderString(g, "main", "No tags")
|
||||
}
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Tags.SelectedLine, len(gui.State.Tags), v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := gui.OSCommand.ExecutableFromString(
|
||||
gui.GitCommand.GetBranchGraphCmdStr(tag.Name),
|
||||
)
|
||||
if err := gui.newCmdTask("main", cmd); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
go func() {
|
||||
show, err := gui.GitCommand.ShowTag(tag.Name)
|
||||
if err != nil {
|
||||
show = ""
|
||||
}
|
||||
|
||||
graph, err := gui.GitCommand.GetBranchGraph(tag.Name)
|
||||
if err != nil {
|
||||
graph = "No graph for tag " + tag.Name
|
||||
}
|
||||
|
||||
_ = gui.renderString(g, "main", fmt.Sprintf("%s\n%s", show, graph))
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -69,10 +78,8 @@ func (gui *Gui) renderTagsWithSelection() error {
|
||||
if err := gui.renderListPanel(branchesView, gui.State.Tags); err != nil {
|
||||
return err
|
||||
}
|
||||
if gui.g.CurrentView() == branchesView && branchesView.Context == "tags" {
|
||||
if err := gui.handleTagSelect(gui.g, branchesView); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gui.handleTagSelect(gui.g, branchesView); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/tasks"
|
||||
)
|
||||
|
||||
func (gui *Gui) newCmdTask(viewName string, cmd *exec.Cmd) error {
|
||||
view, err := gui.g.View(viewName)
|
||||
if err != nil {
|
||||
return nil // swallowing for now
|
||||
}
|
||||
|
||||
_, height := view.Size()
|
||||
_, oy := view.Origin()
|
||||
|
||||
manager := gui.getManager(view)
|
||||
|
||||
if err := manager.NewTask(manager.NewCmdTask(cmd, height+oy+10)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) newTask(viewName string, f func(chan struct{}) error) error {
|
||||
view, err := gui.g.View(viewName)
|
||||
if err != nil {
|
||||
return nil // swallowing for now
|
||||
}
|
||||
|
||||
manager := gui.getManager(view)
|
||||
|
||||
if err := manager.NewTask(f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) newStringTask(viewName string, str string) error {
|
||||
view, err := gui.g.View(viewName)
|
||||
if err != nil {
|
||||
return nil // swallowing for now
|
||||
}
|
||||
|
||||
manager := gui.getManager(view)
|
||||
|
||||
f := func(stop chan struct{}) error {
|
||||
return gui.renderString(gui.g, viewName, str)
|
||||
}
|
||||
|
||||
if err := manager.NewTask(f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager {
|
||||
manager, ok := gui.viewBufferManagerMap[view.Name()]
|
||||
if !ok {
|
||||
manager = tasks.NewViewBufferManager(
|
||||
gui.Log,
|
||||
view,
|
||||
func() {
|
||||
view.Clear()
|
||||
},
|
||||
func() {
|
||||
gui.g.Update(func(*gocui.Gui) error { return nil })
|
||||
})
|
||||
gui.viewBufferManagerMap[view.Name()] = manager
|
||||
}
|
||||
|
||||
return manager
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
stop chan struct{}
|
||||
stopped bool
|
||||
stopMutex sync.Mutex
|
||||
notifyStopped chan struct{}
|
||||
Log *logrus.Entry
|
||||
f func(chan struct{}) error
|
||||
}
|
||||
|
||||
type ViewBufferManager struct {
|
||||
writer io.Writer
|
||||
waitingTask *Task
|
||||
currentTask *Task
|
||||
waitingMutex sync.Mutex
|
||||
taskIDMutex sync.Mutex
|
||||
Log *logrus.Entry
|
||||
newTaskId int
|
||||
readLines chan int
|
||||
|
||||
// beforeStart is the function that is called before starting a new task
|
||||
beforeStart func()
|
||||
refreshView func()
|
||||
}
|
||||
|
||||
func NewViewBufferManager(log *logrus.Entry, writer io.Writer, beforeStart func(), refreshView func()) *ViewBufferManager {
|
||||
return &ViewBufferManager{Log: log, writer: writer, beforeStart: beforeStart, refreshView: refreshView, readLines: make(chan int, 1024)}
|
||||
}
|
||||
|
||||
func (m *ViewBufferManager) ReadLines(n int) {
|
||||
go func() {
|
||||
m.readLines <- n
|
||||
}()
|
||||
}
|
||||
|
||||
func (m *ViewBufferManager) NewCmdTask(cmd *exec.Cmd, linesToRead int) func(chan struct{}) error {
|
||||
return func(stop chan struct{}) error {
|
||||
r, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stderr = cmd.Stdout
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-stop
|
||||
if cmd.ProcessState.ExitCode() == -1 {
|
||||
if err := kill(cmd); err != nil {
|
||||
m.Log.Warn(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// not sure if it's the right move to redefine this or not
|
||||
m.readLines = make(chan int, 1024)
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
loaded := false
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
defer ticker.Stop()
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if !loaded {
|
||||
m.beforeStart()
|
||||
m.writer.Write([]byte("loading..."))
|
||||
m.refreshView()
|
||||
}
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
outer:
|
||||
for {
|
||||
select {
|
||||
case linesToRead := <-m.readLines:
|
||||
for i := 0; i < linesToRead; i++ {
|
||||
ok := scanner.Scan()
|
||||
if !loaded {
|
||||
m.beforeStart()
|
||||
loaded = true
|
||||
}
|
||||
|
||||
select {
|
||||
case <-stop:
|
||||
break outer
|
||||
default:
|
||||
}
|
||||
if !ok {
|
||||
m.refreshView()
|
||||
break outer
|
||||
}
|
||||
m.writer.Write(append(scanner.Bytes(), []byte("\n")...))
|
||||
}
|
||||
m.refreshView()
|
||||
case <-stop:
|
||||
break outer
|
||||
}
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
m.Log.Warn(err)
|
||||
}
|
||||
|
||||
close(done)
|
||||
}()
|
||||
|
||||
m.readLines <- linesToRead
|
||||
|
||||
<-done
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the task manager, killing whatever task may currently be running
|
||||
func (t *ViewBufferManager) Close() {
|
||||
if t.currentTask == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := make(chan struct{}, 1)
|
||||
|
||||
go func() {
|
||||
t.currentTask.Stop()
|
||||
c <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
return
|
||||
case <-time.After(3 * time.Second):
|
||||
fmt.Println("cannot kill child process")
|
||||
}
|
||||
}
|
||||
|
||||
// different kinds of tasks:
|
||||
// 1) command based, where the manager can be asked to read more lines, but the command can be killed
|
||||
// 2) string based, where the manager can also be asked to read more lines
|
||||
|
||||
func (m *ViewBufferManager) NewTask(f func(stop chan struct{}) error) error {
|
||||
go func() {
|
||||
m.taskIDMutex.Lock()
|
||||
m.newTaskId++
|
||||
taskID := m.newTaskId
|
||||
m.Log.Infof("starting task %d", taskID)
|
||||
m.taskIDMutex.Unlock()
|
||||
|
||||
m.waitingMutex.Lock()
|
||||
defer m.waitingMutex.Unlock()
|
||||
if taskID < m.newTaskId {
|
||||
return
|
||||
}
|
||||
|
||||
stop := make(chan struct{})
|
||||
notifyStopped := make(chan struct{})
|
||||
|
||||
if m.currentTask != nil {
|
||||
m.Log.Info("asking task to stop")
|
||||
m.currentTask.Stop()
|
||||
m.Log.Info("task stopped")
|
||||
}
|
||||
|
||||
m.currentTask = &Task{
|
||||
stop: stop,
|
||||
notifyStopped: notifyStopped,
|
||||
Log: m.Log,
|
||||
f: f,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := f(stop); err != nil {
|
||||
m.Log.Error(err) // might need an onError callback
|
||||
}
|
||||
|
||||
m.Log.Infof("returning from task %d", taskID)
|
||||
close(notifyStopped)
|
||||
}()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) Stop() {
|
||||
t.stopMutex.Lock()
|
||||
defer t.stopMutex.Unlock()
|
||||
if t.stopped {
|
||||
return
|
||||
}
|
||||
close(t.stop)
|
||||
t.Log.Info("closed stop channel, waiting for notifyStopped message")
|
||||
<-t.notifyStopped
|
||||
t.Log.Info("received notifystopped message")
|
||||
t.stopped = true
|
||||
return
|
||||
}
|
||||
|
||||
// kill kills a process
|
||||
func kill(cmd *exec.Cmd) error {
|
||||
if cmd.Process == nil {
|
||||
// somebody got to it before we were able to, poor bastard
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd.Process.Kill()
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package test
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -44,15 +43,3 @@ func CreateMockCommand(t *testing.T, swappers []*CommandSwapper) func(cmd string
|
||||
return command
|
||||
}
|
||||
}
|
||||
|
||||
func AssertContainsMatch(t *testing.T, strs []string, pattern *regexp.Regexp, message string) {
|
||||
t.Helper()
|
||||
|
||||
for _, str := range strs {
|
||||
if pattern.Match([]byte(str)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
assert.Fail(t, message)
|
||||
}
|
||||
|
||||
17
vendor/github.com/jesseduffield/gocui/escape.go
generated
vendored
17
vendor/github.com/jesseduffield/gocui/escape.go
generated
vendored
@@ -5,10 +5,8 @@
|
||||
package gocui
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type escapeInterpreter struct {
|
||||
@@ -17,7 +15,6 @@ type escapeInterpreter struct {
|
||||
csiParam []string
|
||||
curFgColor, curBgColor Attribute
|
||||
mode OutputMode
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type escapeState int
|
||||
@@ -37,9 +34,6 @@ var (
|
||||
|
||||
// runes in case of error will output the non-parsed runes as a string.
|
||||
func (ei *escapeInterpreter) runes() []rune {
|
||||
ei.mutex.Lock()
|
||||
defer ei.mutex.Unlock()
|
||||
|
||||
switch ei.state {
|
||||
case stateNone:
|
||||
return []rune{0x1b}
|
||||
@@ -72,9 +66,6 @@ func newEscapeInterpreter(mode OutputMode) *escapeInterpreter {
|
||||
|
||||
// reset sets the escapeInterpreter in initial state.
|
||||
func (ei *escapeInterpreter) reset() {
|
||||
ei.mutex.Lock()
|
||||
defer ei.mutex.Unlock()
|
||||
|
||||
ei.state = stateNone
|
||||
ei.curFgColor = ColorDefault
|
||||
ei.curBgColor = ColorDefault
|
||||
@@ -85,9 +76,6 @@ func (ei *escapeInterpreter) reset() {
|
||||
// of an escape sequence, and as such should not be printed verbatim. Otherwise,
|
||||
// it's not an escape sequence.
|
||||
func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
|
||||
ei.mutex.Lock()
|
||||
defer ei.mutex.Unlock()
|
||||
|
||||
// Sanity checks
|
||||
if len(ei.csiParam) > 20 {
|
||||
return false, errCSITooLong
|
||||
@@ -191,9 +179,6 @@ func (ei *escapeInterpreter) outputNormal() error {
|
||||
// 0x11 - 0xe8: 216 different colors
|
||||
// 0xe9 - 0x1ff: 24 different shades of grey
|
||||
func (ei *escapeInterpreter) output256() error {
|
||||
ei.mutex.Lock()
|
||||
defer ei.mutex.Unlock()
|
||||
|
||||
if len(ei.csiParam) < 3 {
|
||||
return ei.outputNormal()
|
||||
}
|
||||
|
||||
33
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
33
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
@@ -7,7 +7,6 @@ package gocui
|
||||
import (
|
||||
standardErrors "errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
@@ -90,10 +89,6 @@ type Gui struct {
|
||||
// SupportOverlaps is true when we allow for view edges to overlap with other
|
||||
// view edges
|
||||
SupportOverlaps bool
|
||||
|
||||
// tickingMutex ensures we don't have two loops ticking. The point of 'ticking'
|
||||
// is to refresh the gui rapidly so that loader characters can be animated.
|
||||
tickingMutex sync.Mutex
|
||||
}
|
||||
|
||||
// NewGui returns a new Gui object with a given output mode.
|
||||
@@ -130,7 +125,9 @@ func NewGui(mode OutputMode, supportOverlaps bool) (*Gui, error) {
|
||||
// Close finalizes the library. It should be called after a successful
|
||||
// initialization and when gocui is not needed anymore.
|
||||
func (g *Gui) Close() {
|
||||
close(g.stop)
|
||||
go func() {
|
||||
g.stop <- struct{}{}
|
||||
}()
|
||||
termbox.Close()
|
||||
}
|
||||
|
||||
@@ -412,6 +409,7 @@ func (g *Gui) SetManagerFunc(manager func(*Gui) error) {
|
||||
// MainLoop runs the main loop until an error is returned. A successful
|
||||
// finish should return ErrQuit.
|
||||
func (g *Gui) MainLoop() error {
|
||||
g.loaderTick()
|
||||
if err := g.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -837,25 +835,14 @@ func (g *Gui) execKeybinding(v *View, kb *keybinding) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (g *Gui) StartTicking() {
|
||||
func (g *Gui) loaderTick() {
|
||||
go func() {
|
||||
g.tickingMutex.Lock()
|
||||
defer g.tickingMutex.Unlock()
|
||||
ticker := time.NewTicker(time.Millisecond * 50)
|
||||
defer ticker.Stop()
|
||||
outer:
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
for _, view := range g.Views() {
|
||||
if view.HasLoader {
|
||||
g.userEvents <- userEvent{func(g *Gui) error { return nil }}
|
||||
continue outer
|
||||
}
|
||||
for range time.Tick(time.Millisecond * 50) {
|
||||
for _, view := range g.Views() {
|
||||
if view.HasLoader {
|
||||
g.userEvents <- userEvent{func(g *Gui) error { return nil }}
|
||||
break
|
||||
}
|
||||
return
|
||||
case <-g.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
4
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
4
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
@@ -458,8 +458,6 @@ func (v *View) clearRunes() {
|
||||
// BufferLines returns the lines in the view's internal
|
||||
// buffer.
|
||||
func (v *View) BufferLines() []string {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
lines := make([]string, len(v.lines))
|
||||
for i, l := range v.lines {
|
||||
str := lineType(l).String()
|
||||
@@ -478,8 +476,6 @@ func (v *View) Buffer() string {
|
||||
// ViewBufferLines returns the lines in the view's internal
|
||||
// buffer that is shown to the user.
|
||||
func (v *View) ViewBufferLines() []string {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
lines := make([]string, len(v.viewLines))
|
||||
for i, l := range v.viewLines {
|
||||
str := lineType(l.line).String()
|
||||
|
||||
1
vendor/github.com/jesseduffield/roll/.gitignore
generated
vendored
Normal file
1
vendor/github.com/jesseduffield/roll/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
rollbar.test
|
||||
24
vendor/github.com/jesseduffield/roll/CHANGELOG
generated
vendored
Normal file
24
vendor/github.com/jesseduffield/roll/CHANGELOG
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
0.2.0 - May 22nd, 2016
|
||||
====================
|
||||
|
||||
* Do not use title to determine fingerprint.
|
||||
|
||||
0.1.1 - August 24th, 2016
|
||||
=========================
|
||||
|
||||
* Fix Go 1.6 support by removing call to runtime.CallersFrames, which was added
|
||||
in Go 1.7.
|
||||
|
||||
0.1.0 - August 23rd, 2016
|
||||
=========================
|
||||
|
||||
* Allow passing in arbitrary function pointer stacks. (thanks @apg!)
|
||||
* Remove unneeded exported constants.
|
||||
* Make HTTP(S) endpoint configurable.
|
||||
* Remove unneeded debug print statement.
|
||||
|
||||
0.0.1 - January 19th, 2015
|
||||
==========================
|
||||
|
||||
* Initial release based on https://github.com/stvp/rollbar
|
||||
|
||||
22
vendor/github.com/jesseduffield/roll/LICENSE
generated
vendored
Normal file
22
vendor/github.com/jesseduffield/roll/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2016 Stovepipe Studios
|
||||
|
||||
MIT License
|
||||
|
||||
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.
|
||||
53
vendor/github.com/jesseduffield/roll/README.md
generated
vendored
Normal file
53
vendor/github.com/jesseduffield/roll/README.md
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
roll
|
||||
----
|
||||
|
||||
`roll` is a basic Rollbar client for Go that reports errors and logs
|
||||
messages. It automatically builds stack traces and also supports
|
||||
arbitrary traces. All errors and messages are sent to Rollbar
|
||||
synchronously.
|
||||
|
||||
`roll` is intentionally simple. For more advanced functionality, check
|
||||
out [heroku/rollbar](https://github.com/heroku/rollbar).
|
||||
|
||||
[API docs on godoc.org](http://godoc.org/github.com/stvp/roll)
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
* Critical-, Error-, and Warning-level messages include a stack trace.
|
||||
However, Go's `error` type doesn't include stack information from the
|
||||
location the error was set or allocated. Instead, `roll` uses the
|
||||
stack information from where the error was reported.
|
||||
* Info- and Debug-level Rollbar messages do not include stack traces.
|
||||
* When calling `roll` away from where the error actually occurred,
|
||||
`roll`'s stack walking won't represent the actual stack trace at the
|
||||
time the error occurred. The `*Stack` variants of Critical, Error, and
|
||||
Warning take a `[]uintptr`, allowing the stack to be provided, rather
|
||||
than walked.
|
||||
|
||||
Running Tests
|
||||
=============
|
||||
|
||||
`go test` will run tests against a fake server by default.
|
||||
|
||||
If the environment variable `TOKEN` is a Rollbar access token, running
|
||||
`go test` will produce errors using an environment named `test`.
|
||||
|
||||
TOKEN=f0df01587b8f76b2c217af34c479f9ea go test
|
||||
|
||||
Verify the reported errors manually in the Rollbar dashboard.
|
||||
|
||||
Contributors
|
||||
============
|
||||
|
||||
* @challiwill
|
||||
* @tysonmote
|
||||
* @apg
|
||||
|
||||
This library was forked from [stvp/rollbar](https://github.com/stvp/rollbar),
|
||||
which had contributions from:
|
||||
|
||||
* @kjk
|
||||
* @Soulou
|
||||
* @paulmach
|
||||
|
||||
242
vendor/github.com/jesseduffield/roll/client.go
generated
vendored
Normal file
242
vendor/github.com/jesseduffield/roll/client.go
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
package roll
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// By default, all Rollbar API requests are sent to this endpoint.
|
||||
endpoint = "https://api.rollbar.com/api/1/item/"
|
||||
|
||||
// Identify this Rollbar client library to the Rollbar API.
|
||||
clientName = "go-roll"
|
||||
clientVersion = "0.2.0"
|
||||
clientLanguage = "go"
|
||||
)
|
||||
|
||||
var (
|
||||
// Endpoint is the default HTTP(S) endpoint that all Rollbar API requests
|
||||
// will be sent to. By default, this is Rollbar's "Items" API endpoint. If
|
||||
// this is blank, no items will be sent to Rollbar.
|
||||
Endpoint = endpoint
|
||||
|
||||
// Rollbar access token for the global client. If this is blank, no items
|
||||
// will be sent to Rollbar.
|
||||
Token = ""
|
||||
|
||||
// Environment for all items reported with the global client.
|
||||
Environment = "development"
|
||||
)
|
||||
|
||||
type rollbarSuccess struct {
|
||||
Result map[string]string `json:"result"`
|
||||
}
|
||||
|
||||
// Client reports items to a single Rollbar project.
|
||||
type Client interface {
|
||||
Critical(err error, custom map[string]string) (uuid string, e error)
|
||||
CriticalStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error)
|
||||
Error(err error, custom map[string]string) (uuid string, e error)
|
||||
ErrorStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error)
|
||||
Warning(err error, custom map[string]string) (uuid string, e error)
|
||||
WarningStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error)
|
||||
Info(msg string, custom map[string]string) (uuid string, e error)
|
||||
Debug(msg string, custom map[string]string) (uuid string, e error)
|
||||
}
|
||||
|
||||
type rollbarClient struct {
|
||||
token string
|
||||
env string
|
||||
}
|
||||
|
||||
// New creates a new Rollbar client that reports items to the given project
|
||||
// token and with the given environment (eg. "production", "development", etc).
|
||||
func New(token, env string) Client {
|
||||
return &rollbarClient{token, env}
|
||||
}
|
||||
|
||||
func Critical(err error, custom map[string]string) (uuid string, e error) {
|
||||
return CriticalStack(err, getCallers(2), custom)
|
||||
}
|
||||
|
||||
func CriticalStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) {
|
||||
return New(Token, Environment).CriticalStack(err, ptrs, custom)
|
||||
}
|
||||
|
||||
func Error(err error, custom map[string]string) (uuid string, e error) {
|
||||
return ErrorStack(err, getCallers(2), custom)
|
||||
}
|
||||
|
||||
func ErrorStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) {
|
||||
return New(Token, Environment).ErrorStack(err, ptrs, custom)
|
||||
}
|
||||
|
||||
func Warning(err error, custom map[string]string) (uuid string, e error) {
|
||||
return WarningStack(err, getCallers(2), custom)
|
||||
}
|
||||
|
||||
func WarningStack(err error, ptrs []uintptr, custom map[string]string) (uuid string, e error) {
|
||||
return New(Token, Environment).WarningStack(err, ptrs, custom)
|
||||
}
|
||||
|
||||
func Info(msg string, custom map[string]string) (uuid string, e error) {
|
||||
return New(Token, Environment).Info(msg, custom)
|
||||
}
|
||||
|
||||
func Debug(msg string, custom map[string]string) (uuid string, e error) {
|
||||
return New(Token, Environment).Debug(msg, custom)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) Critical(err error, custom map[string]string) (uuid string, e error) {
|
||||
return c.CriticalStack(err, getCallers(2), custom)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) CriticalStack(err error, callers []uintptr, custom map[string]string) (uuid string, e error) {
|
||||
item := c.buildTraceItem("critical", err, callers, custom)
|
||||
return c.send(item)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) Error(err error, custom map[string]string) (uuid string, e error) {
|
||||
return c.ErrorStack(err, getCallers(2), custom)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) ErrorStack(err error, callers []uintptr, custom map[string]string) (uuid string, e error) {
|
||||
item := c.buildTraceItem("error", err, callers, custom)
|
||||
return c.send(item)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) Warning(err error, custom map[string]string) (uuid string, e error) {
|
||||
return c.WarningStack(err, getCallers(2), custom)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) WarningStack(err error, callers []uintptr, custom map[string]string) (uuid string, e error) {
|
||||
item := c.buildTraceItem("warning", err, callers, custom)
|
||||
return c.send(item)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) Info(msg string, custom map[string]string) (uuid string, e error) {
|
||||
item := c.buildMessageItem("info", msg, custom)
|
||||
return c.send(item)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) Debug(msg string, custom map[string]string) (uuid string, e error) {
|
||||
item := c.buildMessageItem("debug", msg, custom)
|
||||
return c.send(item)
|
||||
}
|
||||
|
||||
func (c *rollbarClient) buildTraceItem(level string, err error, callers []uintptr, custom map[string]string) (item map[string]interface{}) {
|
||||
stack := buildRollbarFrames(callers)
|
||||
item = c.buildItem(level, err.Error(), custom)
|
||||
itemData := item["data"].(map[string]interface{})
|
||||
itemData["fingerprint"] = stack.fingerprint()
|
||||
itemData["body"] = map[string]interface{}{
|
||||
"trace": map[string]interface{}{
|
||||
"frames": stack,
|
||||
"exception": map[string]interface{}{
|
||||
"class": errorClass(err),
|
||||
"message": err.Error(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
func (c *rollbarClient) buildMessageItem(level string, msg string, custom map[string]string) (item map[string]interface{}) {
|
||||
item = c.buildItem(level, msg, custom)
|
||||
itemData := item["data"].(map[string]interface{})
|
||||
itemData["body"] = map[string]interface{}{
|
||||
"message": map[string]interface{}{
|
||||
"body": msg,
|
||||
},
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
func (c *rollbarClient) buildItem(level, title string, custom map[string]string) map[string]interface{} {
|
||||
hostname, _ := os.Hostname()
|
||||
|
||||
return map[string]interface{}{
|
||||
"access_token": c.token,
|
||||
"data": map[string]interface{}{
|
||||
"environment": c.env,
|
||||
"title": title,
|
||||
"level": level,
|
||||
"timestamp": time.Now().Unix(),
|
||||
"platform": runtime.GOOS,
|
||||
"language": clientLanguage,
|
||||
"server": map[string]interface{}{
|
||||
"host": hostname,
|
||||
},
|
||||
"notifier": map[string]interface{}{
|
||||
"name": clientName,
|
||||
"version": clientVersion,
|
||||
},
|
||||
"custom": custom,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// send reports the given item to Rollbar and returns either a UUID for the
|
||||
// reported item or an error.
|
||||
func (c *rollbarClient) send(item map[string]interface{}) (uuid string, err error) {
|
||||
if len(c.token) == 0 || len(Endpoint) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
jsonBody, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := http.Post(Endpoint, "application/json", bytes.NewReader(jsonBody))
|
||||
if err != nil {
|
||||
// If something goes wrong it really does not matter
|
||||
return "", nil
|
||||
}
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// If something goes wrong it really does not matter
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Extract UUID from JSON response
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
success := rollbarSuccess{}
|
||||
json.Unmarshal(body, &success)
|
||||
|
||||
return success.Result["uuid"], nil
|
||||
}
|
||||
|
||||
// errorClass returns a class name for an error (eg. "ErrUnexpectedEOF"). For
|
||||
// string errors, it returns an Adler-32 checksum of the error string.
|
||||
func errorClass(err error) string {
|
||||
class := reflect.TypeOf(err).String()
|
||||
if class == "" {
|
||||
return "panic"
|
||||
} else if class == "*errors.errorString" {
|
||||
checksum := adler32.Checksum([]byte(err.Error()))
|
||||
return fmt.Sprintf("{%x}", checksum)
|
||||
} else {
|
||||
return strings.TrimPrefix(class, "*")
|
||||
}
|
||||
}
|
||||
98
vendor/github.com/jesseduffield/roll/stack.go
generated
vendored
Normal file
98
vendor/github.com/jesseduffield/roll/stack.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
package roll
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
knownFilePathPatterns = []string{
|
||||
"github.com/",
|
||||
"code.google.com/",
|
||||
"bitbucket.org/",
|
||||
"launchpad.net/",
|
||||
"gopkg.in/",
|
||||
}
|
||||
)
|
||||
|
||||
func getCallers(skip int) (pc []uintptr) {
|
||||
pc = make([]uintptr, 1000)
|
||||
i := runtime.Callers(skip+1, pc)
|
||||
return pc[0:i]
|
||||
}
|
||||
|
||||
// -- rollbarFrames
|
||||
|
||||
type rollbarFrame struct {
|
||||
Filename string `json:"filename"`
|
||||
Method string `json:"method"`
|
||||
Line int `json:"lineno"`
|
||||
}
|
||||
|
||||
type rollbarFrames []rollbarFrame
|
||||
|
||||
// buildRollbarFrames takes a slice of function pointers and returns a Rollbar
|
||||
// API payload containing the filename, method name, and line number of each
|
||||
// function.
|
||||
func buildRollbarFrames(callers []uintptr) (frames rollbarFrames) {
|
||||
frames = rollbarFrames{}
|
||||
|
||||
// 2016-08-24 - runtime.CallersFrames was added in Go 1.7, which should
|
||||
// replace the following code when roll is able to require Go 1.7+.
|
||||
for _, caller := range callers {
|
||||
frame := rollbarFrame{
|
||||
Filename: "???",
|
||||
Method: "???",
|
||||
}
|
||||
if fn := runtime.FuncForPC(caller); fn != nil {
|
||||
name, line := fn.FileLine(caller)
|
||||
frame.Filename = scrubFile(name)
|
||||
frame.Line = line
|
||||
frame.Method = scrubFunction(fn.Name())
|
||||
}
|
||||
frames = append(frames, frame)
|
||||
}
|
||||
|
||||
return frames
|
||||
}
|
||||
|
||||
// fingerprint returns a checksum that uniquely identifies a stacktrace by the
|
||||
// filename, method name, and line number of every frame in the stack.
|
||||
func (f rollbarFrames) fingerprint() string {
|
||||
hash := crc32.NewIEEE()
|
||||
for _, frame := range f {
|
||||
fmt.Fprintf(hash, "%s%s%d", frame.Filename, frame.Method, frame.Line)
|
||||
}
|
||||
return fmt.Sprintf("%x", hash.Sum32())
|
||||
}
|
||||
|
||||
// -- Helpers
|
||||
|
||||
// scrubFile removes unneeded information from the path of a source file. This
|
||||
// makes them shorter in Rollbar UI as well as making them the same, regardless
|
||||
// of the machine the code was compiled on.
|
||||
//
|
||||
// Example:
|
||||
// /home/foo/go/src/github.com/stvp/roll/rollbar.go -> github.com/stvp/roll/rollbar.go
|
||||
func scrubFile(s string) string {
|
||||
var i int
|
||||
for _, pattern := range knownFilePathPatterns {
|
||||
i = strings.Index(s, pattern)
|
||||
if i != -1 {
|
||||
return s[i:]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// scrubFunction removes unneeded information from the full name of a function.
|
||||
//
|
||||
// Example:
|
||||
// github.com/stvp/roll.getCallers -> roll.getCallers
|
||||
func scrubFunction(name string) string {
|
||||
end := strings.LastIndex(name, string(os.PathSeparator))
|
||||
return name[end+1 : len(name)]
|
||||
}
|
||||
22
vendor/github.com/jesseduffield/rollrus/LICENSE
generated
vendored
Normal file
22
vendor/github.com/jesseduffield/rollrus/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © Heroku 2014 - 2015
|
||||
|
||||
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.
|
||||
15
vendor/github.com/jesseduffield/rollrus/README.md
generated
vendored
Normal file
15
vendor/github.com/jesseduffield/rollrus/README.md
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
[](https://travis-ci.org/heroku/rollrus) [](https://godoc.org/github.com/heroku/rollrus)
|
||||
|
||||
# What
|
||||
|
||||
Rollrus is what happens when [Logrus](https://github.com/sirupsen/logrus) meets [Roll](https://github.com/stvp/roll).
|
||||
|
||||
When a .Error, .Fatal or .Panic logging function is called, report the details to rollbar via a Logrus hook.
|
||||
|
||||
Delivery is synchronous to help ensure that logs are delivered.
|
||||
|
||||
If the error includes a [`StackTrace`](https://godoc.org/github.com/pkg/errors#StackTrace), that `StackTrace` is reported to rollbar.
|
||||
|
||||
# Usage
|
||||
|
||||
Examples available in the [tests](https://github.com/heroku/rollrus/blob/master/rollrus_test.go) or on [GoDoc](https://godoc.org/github.com/heroku/rollrus).
|
||||
7
vendor/github.com/jesseduffield/rollrus/go.mod
generated
vendored
Normal file
7
vendor/github.com/jesseduffield/rollrus/go.mod
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
module github.com/jesseduffield/rollrus
|
||||
|
||||
require (
|
||||
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/sirupsen/logrus v1.3.0
|
||||
)
|
||||
19
vendor/github.com/jesseduffield/rollrus/go.sum
generated
vendored
Normal file
19
vendor/github.com/jesseduffield/rollrus/go.sum
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00 h1:+JaOkfBNYQYlGD7dgru8mCwYNEc5tRRI8mThlVANhSM=
|
||||
github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00/go.mod h1:cWNQljQAWYBp4wchyGfql4q2jRNZXxiE1KhVQgz+JaM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
287
vendor/github.com/jesseduffield/rollrus/rollrus.go
generated
vendored
Normal file
287
vendor/github.com/jesseduffield/rollrus/rollrus.go
generated
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
// Package rollrus combines github.com/jesseduffield/roll with github.com/sirupsen/logrus
|
||||
// via logrus.Hook mechanism, so that whenever logrus' logger.Error/f(),
|
||||
// logger.Fatal/f() or logger.Panic/f() are used the messages are
|
||||
// intercepted and sent to rollbar.
|
||||
//
|
||||
// Using SetupLogging should suffice for basic use cases that use the logrus
|
||||
// singleton logger.
|
||||
//
|
||||
// More custom uses are supported by creating a new Hook with NewHook and
|
||||
// registering that hook with the logrus Logger of choice.
|
||||
//
|
||||
// The levels can be customized with the WithLevels OptionFunc.
|
||||
//
|
||||
// Specific errors can be ignored with the WithIgnoredErrors OptionFunc. This is
|
||||
// useful for ignoring errors such as context.Canceled.
|
||||
//
|
||||
// See the Examples in the tests for more usage.
|
||||
package rollrus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/roll"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var defaultTriggerLevels = []logrus.Level{
|
||||
logrus.ErrorLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.PanicLevel,
|
||||
}
|
||||
|
||||
// Hook is a wrapper for the rollbar Client and is usable as a logrus.Hook.
|
||||
type Hook struct {
|
||||
roll.Client
|
||||
triggers []logrus.Level
|
||||
ignoredErrors []error
|
||||
ignoreErrorFunc func(error) bool
|
||||
ignoreFunc func(error, map[string]string) bool
|
||||
|
||||
// only used for tests to verify whether or not a report happened.
|
||||
reported bool
|
||||
}
|
||||
|
||||
// OptionFunc that can be passed to NewHook.
|
||||
type OptionFunc func(*Hook)
|
||||
|
||||
// wellKnownErrorFields are the names of the fields to be checked for values of
|
||||
// type `error`, in priority order.
|
||||
var wellKnownErrorFields = []string{
|
||||
logrus.ErrorKey, "err",
|
||||
}
|
||||
|
||||
// WithLevels is an OptionFunc that customizes the log.Levels the hook will
|
||||
// report on.
|
||||
func WithLevels(levels ...logrus.Level) OptionFunc {
|
||||
return func(h *Hook) {
|
||||
h.triggers = levels
|
||||
}
|
||||
}
|
||||
|
||||
// WithMinLevel is an OptionFunc that customizes the log.Levels the hook will
|
||||
// report on by selecting all levels more severe than the one provided.
|
||||
func WithMinLevel(level logrus.Level) OptionFunc {
|
||||
var levels []logrus.Level
|
||||
for _, l := range logrus.AllLevels {
|
||||
if l <= level {
|
||||
levels = append(levels, l)
|
||||
}
|
||||
}
|
||||
|
||||
return func(h *Hook) {
|
||||
h.triggers = levels
|
||||
}
|
||||
}
|
||||
|
||||
// WithIgnoredErrors is an OptionFunc that whitelists certain errors to prevent
|
||||
// them from firing. See https://golang.org/ref/spec#Comparison_operators
|
||||
func WithIgnoredErrors(errors ...error) OptionFunc {
|
||||
return func(h *Hook) {
|
||||
h.ignoredErrors = append(h.ignoredErrors, errors...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithIgnoreErrorFunc is an OptionFunc that receives the error that is about
|
||||
// to be logged and returns true/false if it wants to fire a rollbar alert for.
|
||||
func WithIgnoreErrorFunc(fn func(error) bool) OptionFunc {
|
||||
return func(h *Hook) {
|
||||
h.ignoreErrorFunc = fn
|
||||
}
|
||||
}
|
||||
|
||||
// WithIgnoreFunc is an OptionFunc that receives the error and custom fields that are about
|
||||
// to be logged and returns true/false if it wants to fire a rollbar alert for.
|
||||
func WithIgnoreFunc(fn func(err error, fields map[string]string) bool) OptionFunc {
|
||||
return func(h *Hook) {
|
||||
h.ignoreFunc = fn
|
||||
}
|
||||
}
|
||||
|
||||
// NewHook creates a hook that is intended for use with your own logrus.Logger
|
||||
// instance. Uses the defualt report levels defined in wellKnownErrorFields.
|
||||
func NewHook(token string, env string, opts ...OptionFunc) *Hook {
|
||||
h := NewHookForLevels(token, env, defaultTriggerLevels)
|
||||
|
||||
for _, o := range opts {
|
||||
o(h)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// NewHookForLevels provided by the caller. Otherwise works like NewHook.
|
||||
func NewHookForLevels(token string, env string, levels []logrus.Level) *Hook {
|
||||
return &Hook{
|
||||
Client: roll.New(token, env),
|
||||
triggers: levels,
|
||||
ignoredErrors: make([]error, 0),
|
||||
ignoreErrorFunc: func(error) bool { return false },
|
||||
ignoreFunc: func(error, map[string]string) bool { return false },
|
||||
}
|
||||
}
|
||||
|
||||
// SetupLogging for use on Heroku. If token is not an empty string a rollbar
|
||||
// hook is added with the environment set to env. The log formatter is set to a
|
||||
// TextFormatter with timestamps disabled.
|
||||
func SetupLogging(token, env string) {
|
||||
setupLogging(token, env, defaultTriggerLevels)
|
||||
}
|
||||
|
||||
// SetupLoggingForLevels works like SetupLogging, but allows you to
|
||||
// set the levels on which to trigger this hook.
|
||||
func SetupLoggingForLevels(token, env string, levels []logrus.Level) {
|
||||
setupLogging(token, env, levels)
|
||||
}
|
||||
|
||||
func setupLogging(token, env string, levels []logrus.Level) {
|
||||
logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true})
|
||||
|
||||
if token != "" {
|
||||
logrus.AddHook(NewHookForLevels(token, env, levels))
|
||||
}
|
||||
}
|
||||
|
||||
// ReportPanic attempts to report the panic to rollbar using the provided
|
||||
// client and then re-panic. If it can't report the panic it will print an
|
||||
// error to stderr.
|
||||
func (r *Hook) ReportPanic() {
|
||||
if p := recover(); p != nil {
|
||||
if _, err := r.Client.Critical(fmt.Errorf("panic: %q", p), nil); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "reporting_panic=false err=%q\n", err)
|
||||
}
|
||||
panic(p)
|
||||
}
|
||||
}
|
||||
|
||||
// ReportPanic attempts to report the panic to rollbar if the token is set
|
||||
func ReportPanic(token, env string) {
|
||||
if token != "" {
|
||||
h := &Hook{Client: roll.New(token, env)}
|
||||
h.ReportPanic()
|
||||
}
|
||||
}
|
||||
|
||||
// Levels returns the logrus log.Levels that this hook handles
|
||||
func (r *Hook) Levels() []logrus.Level {
|
||||
if r.triggers == nil {
|
||||
return defaultTriggerLevels
|
||||
}
|
||||
return r.triggers
|
||||
}
|
||||
|
||||
// Fire the hook. This is called by Logrus for entries that match the levels
|
||||
// returned by Levels().
|
||||
func (r *Hook) Fire(entry *logrus.Entry) error {
|
||||
trace, cause := extractError(entry)
|
||||
for _, ie := range r.ignoredErrors {
|
||||
if ie == cause {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if r.ignoreErrorFunc(cause) {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := convertFields(entry.Data)
|
||||
if _, exists := m["time"]; !exists {
|
||||
m["time"] = entry.Time.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
if r.ignoreFunc(cause, m) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.report(entry, cause, m, trace)
|
||||
}
|
||||
|
||||
func (r *Hook) report(entry *logrus.Entry, cause error, m map[string]string, trace []uintptr) (err error) {
|
||||
hasTrace := len(trace) > 0
|
||||
level := entry.Level
|
||||
|
||||
r.reported = true
|
||||
|
||||
switch {
|
||||
case hasTrace && level == logrus.FatalLevel:
|
||||
_, err = r.Client.CriticalStack(cause, trace, m)
|
||||
case hasTrace && level == logrus.PanicLevel:
|
||||
_, err = r.Client.CriticalStack(cause, trace, m)
|
||||
case hasTrace && level == logrus.ErrorLevel:
|
||||
_, err = r.Client.ErrorStack(cause, trace, m)
|
||||
case hasTrace && level == logrus.WarnLevel:
|
||||
_, err = r.Client.WarningStack(cause, trace, m)
|
||||
case level == logrus.FatalLevel || level == logrus.PanicLevel:
|
||||
_, err = r.Client.Critical(cause, m)
|
||||
case level == logrus.ErrorLevel:
|
||||
_, err = r.Client.Error(cause, m)
|
||||
case level == logrus.WarnLevel:
|
||||
_, err = r.Client.Warning(cause, m)
|
||||
case level == logrus.InfoLevel:
|
||||
_, err = r.Client.Info(entry.Message, m)
|
||||
case level == logrus.DebugLevel:
|
||||
_, err = r.Client.Debug(entry.Message, m)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// convertFields converts from log.Fields to map[string]string so that we can
|
||||
// report extra fields to Rollbar
|
||||
func convertFields(fields logrus.Fields) map[string]string {
|
||||
m := make(map[string]string)
|
||||
for k, v := range fields {
|
||||
switch t := v.(type) {
|
||||
case time.Time:
|
||||
m[k] = t.Format(time.RFC3339)
|
||||
default:
|
||||
if s, ok := v.(fmt.Stringer); ok {
|
||||
m[k] = s.String()
|
||||
} else {
|
||||
m[k] = fmt.Sprintf("%+v", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// extractError attempts to extract an error from a well known field, err or error
|
||||
func extractError(entry *logrus.Entry) ([]uintptr, error) {
|
||||
var trace []uintptr
|
||||
fields := entry.Data
|
||||
|
||||
type stackTracer interface {
|
||||
StackTrace() errors.StackTrace
|
||||
}
|
||||
|
||||
for _, f := range wellKnownErrorFields {
|
||||
e, ok := fields[f]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
err, ok := e.(error)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
cause := errors.Cause(err)
|
||||
tracer, ok := err.(stackTracer)
|
||||
if ok {
|
||||
return copyStackTrace(tracer.StackTrace()), cause
|
||||
}
|
||||
return trace, cause
|
||||
}
|
||||
|
||||
// when no error found, default to the logged message.
|
||||
return trace, fmt.Errorf(entry.Message)
|
||||
}
|
||||
|
||||
func copyStackTrace(trace errors.StackTrace) (out []uintptr) {
|
||||
for _, frame := range trace {
|
||||
out = append(out, uintptr(frame))
|
||||
}
|
||||
return
|
||||
}
|
||||
39
vendor/github.com/jesseduffield/termbox-go/api.go
generated
vendored
39
vendor/github.com/jesseduffield/termbox-go/api.go
generated
vendored
@@ -2,16 +2,13 @@
|
||||
|
||||
package termbox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
import "github.com/mattn/go-runewidth"
|
||||
import "fmt"
|
||||
import "os"
|
||||
import "os/signal"
|
||||
import "syscall"
|
||||
import "runtime"
|
||||
import "time"
|
||||
|
||||
// public API
|
||||
|
||||
@@ -29,21 +26,13 @@ func Init() error {
|
||||
|
||||
var err error
|
||||
|
||||
if runtime.GOOS == "openbsd" || runtime.GOOS == "freebsd" {
|
||||
out, err = os.OpenFile("/dev/tty", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in = int(out.Fd())
|
||||
} else {
|
||||
out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err = os.OpenFile("/dev/tty", syscall.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = setup_term()
|
||||
|
||||
@@ -2,7 +2,7 @@ go-runewidth
|
||||
============
|
||||
|
||||
[](https://travis-ci.org/mattn/go-runewidth)
|
||||
[](https://codecov.io/gh/mattn/go-runewidth)
|
||||
[](https://coveralls.io/r/mattn/go-runewidth?branch=HEAD)
|
||||
[](http://godoc.org/github.com/mattn/go-runewidth)
|
||||
[](https://goreportcard.com/report/github.com/mattn/go-runewidth)
|
||||
|
||||
12
vendor/github.com/mattn/go-runewidth/go.test.sh
generated
vendored
12
vendor/github.com/mattn/go-runewidth/go.test.sh
generated
vendored
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
echo "" > coverage.txt
|
||||
|
||||
for d in $(go list ./... | grep -v vendor); do
|
||||
go test -race -coverprofile=profile.out -covermode=atomic "$d"
|
||||
if [ -f profile.out ]; then
|
||||
cat profile.out >> coverage.txt
|
||||
rm profile.out
|
||||
fi
|
||||
done
|
||||
1
vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
1
vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
@@ -50,6 +50,7 @@ func inTables(r rune, ts ...table) bool {
|
||||
}
|
||||
|
||||
func inTable(r rune, t table) bool {
|
||||
// func (t table) IncludesRune(r rune) bool {
|
||||
if r < t[0].first {
|
||||
return false
|
||||
}
|
||||
|
||||
2
vendor/github.com/mattn/go-runewidth/runewidth_table.go
generated
vendored
2
vendor/github.com/mattn/go-runewidth/runewidth_table.go
generated
vendored
@@ -1,5 +1,3 @@
|
||||
// Code generated by script/generate.go. DO NOT EDIT.
|
||||
|
||||
package runewidth
|
||||
|
||||
var combining = table{
|
||||
|
||||
24
vendor/github.com/pkg/errors/.gitignore
generated
vendored
Normal file
24
vendor/github.com/pkg/errors/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
52
vendor/github.com/pkg/errors/README.md
generated
vendored
Normal file
52
vendor/github.com/pkg/errors/README.md
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors) [](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||
|
||||
Package errors provides simple error handling primitives.
|
||||
|
||||
`go get github.com/pkg/errors`
|
||||
|
||||
The traditional error handling idiom in Go is roughly akin to
|
||||
```go
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
|
||||
|
||||
## Adding context to an error
|
||||
|
||||
The errors.Wrap function returns a new error that adds context to the original error. For example
|
||||
```go
|
||||
_, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read failed")
|
||||
}
|
||||
```
|
||||
## Retrieving the cause of an error
|
||||
|
||||
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
|
||||
```go
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
```
|
||||
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
|
||||
```go
|
||||
switch err := errors.Cause(err).(type) {
|
||||
case *MyError:
|
||||
// handle specifically
|
||||
default:
|
||||
// unknown error
|
||||
}
|
||||
```
|
||||
|
||||
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
|
||||
|
||||
Before proposing a change, please discuss your change by raising an issue.
|
||||
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
||||
32
vendor/github.com/pkg/errors/appveyor.yml
generated
vendored
Normal file
32
vendor/github.com/pkg/errors/appveyor.yml
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
version: build-{build}.{branch}
|
||||
|
||||
clone_folder: C:\gopath\src\github.com\pkg\errors
|
||||
shallow_clone: true # for startup speed
|
||||
|
||||
environment:
|
||||
GOPATH: C:\gopath
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
# http://www.appveyor.com/docs/installed-software
|
||||
install:
|
||||
# some helpful output for debugging builds
|
||||
- go version
|
||||
- go env
|
||||
# pre-installed MinGW at C:\MinGW is 32bit only
|
||||
# but MSYS2 at C:\msys64 has mingw64
|
||||
- set PATH=C:\msys64\mingw64\bin;%PATH%
|
||||
- gcc --version
|
||||
- g++ --version
|
||||
|
||||
build_script:
|
||||
- go install -v ./...
|
||||
|
||||
test_script:
|
||||
- set PATH=C:\gopath\bin;%PATH%
|
||||
- go test -v ./...
|
||||
|
||||
#artifacts:
|
||||
# - path: '%GOPATH%\bin\*.exe'
|
||||
deploy: off
|
||||
282
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
282
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which when applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
//
|
||||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// together with the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required, the errors.WithStack and
|
||||
// errors.WithMessage functions destructure errors.Wrap into its component
|
||||
// operations: annotating an error with a stack trace and with a message,
|
||||
// respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
// preceding error. Depending on the nature of the error it may be necessary
|
||||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error that does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
// case *MyError:
|
||||
// // handle specifically
|
||||
// default:
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// Although the causer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported:
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively.
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
//
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface:
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// The returned errors.StackTrace type is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
// The Frame type represents a call site in the stack trace. Frame supports
|
||||
// the fmt.Formatter interface that can be used for printing information about
|
||||
// the stack trace of this error. For example:
|
||||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d", f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Although the stackTracer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, w.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagef annotates err with the format specifier.
|
||||
// If err is nil, WithMessagef returns nil.
|
||||
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
||||
147
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
147
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
pc := f.pc()
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
io.WriteString(s, "unknown")
|
||||
} else {
|
||||
file, _ := fn.FileLine(pc)
|
||||
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
fmt.Fprintf(s, "%d", f.line())
|
||||
case 'n':
|
||||
name := runtime.FuncForPC(f.pc()).Name()
|
||||
io.WriteString(s, funcname(name))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
fmt.Fprintf(s, "\n%+v", f)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
fmt.Fprintf(s, "%v", []Frame(st))
|
||||
}
|
||||
case 's':
|
||||
fmt.Fprintf(s, "%s", []Frame(st))
|
||||
}
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i] = Frame((*s)[i])
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
12
vendor/modules.txt
vendored
12
vendor/modules.txt
vendored
@@ -32,11 +32,15 @@ github.com/hashicorp/hcl/json/token
|
||||
github.com/integrii/flaggy
|
||||
# github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
|
||||
github.com/jbenet/go-context/io
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20200201013258-57fdcf23edc5
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20191116013947-b13bda319532
|
||||
github.com/jesseduffield/gocui
|
||||
# github.com/jesseduffield/pty v1.2.1
|
||||
github.com/jesseduffield/pty
|
||||
# github.com/jesseduffield/termbox-go v0.0.0-20200130214842-1d31d1faa3c9
|
||||
# github.com/jesseduffield/roll v0.0.0-20190629104057-695be2e62b00
|
||||
github.com/jesseduffield/roll
|
||||
# github.com/jesseduffield/rollrus v0.0.0-20190701125922-dd028cb0bfd7
|
||||
github.com/jesseduffield/rollrus
|
||||
# github.com/jesseduffield/termbox-go v0.0.0-20190630083001-9dd53af7214e
|
||||
github.com/jesseduffield/termbox-go
|
||||
# github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/kardianos/osext
|
||||
@@ -50,7 +54,7 @@ github.com/magiconair/properties
|
||||
github.com/mattn/go-colorable
|
||||
# github.com/mattn/go-isatty v0.0.11
|
||||
github.com/mattn/go-isatty
|
||||
# github.com/mattn/go-runewidth v0.0.8
|
||||
# github.com/mattn/go-runewidth v0.0.7
|
||||
github.com/mattn/go-runewidth
|
||||
# github.com/mgutz/str v1.2.0
|
||||
github.com/mgutz/str
|
||||
@@ -64,6 +68,8 @@ github.com/nicksnyder/go-i18n/v2/internal
|
||||
github.com/nicksnyder/go-i18n/v2/internal/plural
|
||||
# github.com/pelletier/go-toml v1.6.0
|
||||
github.com/pelletier/go-toml
|
||||
# github.com/pkg/errors v0.8.1
|
||||
github.com/pkg/errors
|
||||
# github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/pmezard/go-difflib/difflib
|
||||
# github.com/sergi/go-diff v1.0.0
|
||||
|
||||
Reference in New Issue
Block a user