mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-05 15:15:49 +02:00
Merge branch 'hotfix/cursor-positioning' into feature/recent-repos
This commit is contained in:
commit
768b9453f8
@ -2,14 +2,24 @@ version: 2
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.10
|
- image: circleci/golang:1.11
|
||||||
|
|
||||||
working_directory: /go/src/github.com/jesseduffield/lazygit
|
working_directory: /go/src/github.com/jesseduffield/lazygit
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- run:
|
||||||
keys:
|
name: Ensure go.mod file is up to date
|
||||||
- pkg-cache-{{ checksum "Gopkg.lock" }}-v1
|
command: |
|
||||||
|
export GO111MODULE=on
|
||||||
|
rm go.sum
|
||||||
|
mv go.mod /tmp/
|
||||||
|
go mod init
|
||||||
|
export GO111MODULE=auto
|
||||||
|
|
||||||
|
if [ $(diff /tmp/go.mod go.mod|wc -l) -gt 0 ]; then
|
||||||
|
diff /tmp/go.mod go.mod
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
- run:
|
- run:
|
||||||
name: Run gofmt -s
|
name: Run gofmt -s
|
||||||
command: |
|
command: |
|
||||||
@ -17,6 +27,9 @@ jobs:
|
|||||||
find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;
|
find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- pkg-cache-{{ checksum "Gopkg.lock" }}-v2
|
||||||
- run:
|
- run:
|
||||||
name: Run tests
|
name: Run tests
|
||||||
command: |
|
command: |
|
||||||
@ -31,7 +44,7 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
bash <(curl -s https://codecov.io/bash)
|
bash <(curl -s https://codecov.io/bash)
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: pkg-cache-{{ checksum "Gopkg.lock" }}-v1
|
key: pkg-cache-{{ checksum "Gopkg.lock" }}-v2
|
||||||
paths:
|
paths:
|
||||||
- ~/.cache/go-build
|
- ~/.cache/go-build
|
||||||
|
|
||||||
|
17
.gitignore
vendored
17
.gitignore
vendored
@ -3,13 +3,14 @@
|
|||||||
# Logs
|
# Logs
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# Extras
|
# Hidden
|
||||||
extra/lgit.rb
|
.*
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
TODO.*
|
||||||
|
|
||||||
# Notes
|
# Notes
|
||||||
notes/go.notes
|
*.notes
|
||||||
TODO.notes
|
|
||||||
TODO.md
|
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
test/repos/repo
|
test/repos/repo
|
||||||
@ -17,3 +18,9 @@ coverage.txt
|
|||||||
|
|
||||||
# Binaries
|
# Binaries
|
||||||
lazygit
|
lazygit
|
||||||
|
|
||||||
|
# Exceptions
|
||||||
|
!.gitignore
|
||||||
|
!.goreleaser.yml
|
||||||
|
!.circleci/
|
||||||
|
!.github/
|
62
go.mod
Normal file
62
go.mod
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
module github.com/jesseduffield/lazygit
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/aws/aws-sdk-go v1.15.21
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d
|
||||||
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||||
|
github.com/davecgh/go-spew v1.1.0
|
||||||
|
github.com/emirpasic/gods v1.9.0
|
||||||
|
github.com/fatih/color v1.7.0
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7
|
||||||
|
github.com/go-ini/ini v1.38.2
|
||||||
|
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186
|
||||||
|
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001
|
||||||
|
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc
|
||||||
|
github.com/hashicorp/go-version v1.0.0
|
||||||
|
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce
|
||||||
|
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331
|
||||||
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
|
||||||
|
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63
|
||||||
|
github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8
|
||||||
|
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1
|
||||||
|
github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55
|
||||||
|
github.com/magiconair/properties v1.8.0
|
||||||
|
github.com/mattn/go-colorable v0.0.9
|
||||||
|
github.com/mattn/go-isatty v0.0.3
|
||||||
|
github.com/mattn/go-runewidth v0.0.2
|
||||||
|
github.com/mgutz/str v1.2.0
|
||||||
|
github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff
|
||||||
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699
|
||||||
|
github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80
|
||||||
|
github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb
|
||||||
|
github.com/pelletier/go-buffruneio v0.2.0
|
||||||
|
github.com/pelletier/go-toml v1.2.0
|
||||||
|
github.com/pkg/errors v0.8.0
|
||||||
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
|
github.com/sergi/go-diff v1.0.0
|
||||||
|
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0
|
||||||
|
github.com/sirupsen/logrus v1.0.6
|
||||||
|
github.com/spf13/afero v1.1.1
|
||||||
|
github.com/spf13/cast v1.2.0
|
||||||
|
github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834
|
||||||
|
github.com/spf13/pflag v1.0.2
|
||||||
|
github.com/spf13/viper v1.1.0
|
||||||
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||||
|
github.com/src-d/gcfg v1.3.0
|
||||||
|
github.com/stretchr/testify v1.2.2
|
||||||
|
github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea
|
||||||
|
github.com/tcnksm/go-gitconfig v0.1.2
|
||||||
|
github.com/ulikunitz/xz v0.5.4
|
||||||
|
github.com/xanzy/ssh-agent v0.2.0
|
||||||
|
golang.org/x/crypto v0.0.0-20180808211826-de0752318171
|
||||||
|
golang.org/x/net v0.0.0-20180811021610-c39426892332
|
||||||
|
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0
|
||||||
|
golang.org/x/text v0.3.0
|
||||||
|
gopkg.in/src-d/go-billy.v4 v4.2.0
|
||||||
|
gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714
|
||||||
|
gopkg.in/warnings.v0 v0.1.2
|
||||||
|
gopkg.in/yaml.v2 v2.2.1
|
||||||
|
)
|
163
go.sum
Normal file
163
go.sum
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY=
|
||||||
|
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||||
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
|
github.com/aws/aws-sdk-go v1.15.21 h1:STLvc6RrpycslC1NRtTvt/YSgDkIGCTrB9K9vE5R2oQ=
|
||||||
|
github.com/aws/aws-sdk-go v1.15.21/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||||
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
|
||||||
|
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
|
||||||
|
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw=
|
||||||
|
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
|
github.com/go-ini/ini v1.38.2 h1:6Hl/z3p3iFkA0dlDfzYxuFuUGD+kaweypF6btsR2/Q4=
|
||||||
|
github.com/go-ini/ini v1.38.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
|
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
|
||||||
|
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186 h1:URgjUo+bs1KwatoNbwG0uCO4dHN4r1jsp4a5AGgHRjo=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 h1:MFPzqpPED05pFyGjNPJEC2sXM6EHTzFyvX+0s0JoZ48=
|
||||||
|
github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001/go.mod h1:6rdJFnhkXnzGOJbvkrdv4t9nLwKcVA+tmbQeUlkIzrU=
|
||||||
|
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc h1:wAa9fGALVHfjYxZuXRnmuJG2CnwRpJYOTvY6YdErAh0=
|
||||||
|
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||||
|
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
|
||||||
|
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
|
||||||
|
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||||
|
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 h1:qio0y/sQdhbHRA3cmgczo04MaSV2zw+n46G1owvgWIk=
|
||||||
|
github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331/go.mod h1:BT+PgT529opmb6mcUY+Fg0IwVRRmwqFyavEMU17GnBg=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
|
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63 h1:Nrr/yUxNjXWYK0B3IqcFlYh1ICnesJDB4ogcfOVc5Ns=
|
||||||
|
github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63/go.mod h1:fNqjRf+4XnTo2PrGN1JRb79b/BeoHwP4lU00f39SQY0=
|
||||||
|
github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8 h1:YMYnpIu5HUJfx/yfIwnZhFrgWTg51FQWtvZi+PMzQm8=
|
||||||
|
github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
|
||||||
|
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
|
||||||
|
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||||
|
github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55 h1:S38dC4mEwxdw/U41+97VWdbun8mTcTjwg5Ujfg8QPME=
|
||||||
|
github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
|
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||||
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o=
|
||||||
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||||
|
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||||
|
github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff h1:jM4Eo4qMmmcqePS3u6X2lcEELtVuXWkWJIS/pRI3oSk=
|
||||||
|
github.com/mitchellh/go-homedir v0.0.0-20180801233206-58046073cbff/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
||||||
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80/go.mod h1:e4Di5xjP9oTVrC6y3C7C0HoSYXjSbhh/dU0eUV32nB4=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5 h1:/TjjTS4kg7vC+05gD0LE4+97f/+PRFICnK/7wJPk7kE=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5/go.mod h1:4Opqa6/HIv0lhG3WRAkqzO0afezkRhxXI0P8EJkqeRU=
|
||||||
|
github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb h1:YahEjAGkJtCrkqgVHhX6n8ZX+CZ3hDRL9fjLYugLfSs=
|
||||||
|
github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
|
||||||
|
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U=
|
||||||
|
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
|
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
|
||||||
|
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||||
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w=
|
||||||
|
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y=
|
||||||
|
github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
|
||||||
|
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf h1:6V1qxN6Usn4jy8unvggSJz/NC790tefw8Zdy6OZS5co=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
|
||||||
|
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||||
|
github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I=
|
||||||
|
github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
|
||||||
|
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||||
|
github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 h1:kJI9pPzfsULT/72wy7mxkRQZPtKWgFdCA2RTGZ4v8/E=
|
||||||
|
github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
|
||||||
|
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/viper v1.1.0 h1:V7OZpY8i3C1x/pDmU0zNNlfVoDz112fSYvtWMjjS3f4=
|
||||||
|
github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||||
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
|
||||||
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||||
|
github.com/src-d/gcfg v1.3.0 h1:2BEDr8r0I0b8h/fOqwtxCEiq2HJu8n2JGZJQFGXWLjg=
|
||||||
|
github.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||||
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea h1:jysxIKov/4GJ33wI2aRvuIK7yBwB28E5almlgDLPRpM=
|
||||||
|
github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea/go.mod h1:Ffmqrj3nXIMIjeA4uW3Qjj0Ud9eDoTG0fu4JxyAr/tE=
|
||||||
|
github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw=
|
||||||
|
github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
|
||||||
|
github.com/ulikunitz/xz v0.5.4 h1:zATC2OoZ8H1TZll3FpbX+ikwmadbO699PE06cIkm9oU=
|
||||||
|
github.com/ulikunitz/xz v0.5.4/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
|
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
|
||||||
|
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
|
||||||
|
golang.org/x/crypto v0.0.0-20180808211826-de0752318171 h1:vYogbvSFj2YXcjQxFHu/rASSOt9sLytpCaSkiwQ135I=
|
||||||
|
golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E=
|
||||||
|
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0 h1:8H8QZJ30plJyIVj60H3lr8TZGIq2Fh3Cyrs/ZNg1foU=
|
||||||
|
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||||
|
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
|
||||||
|
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||||
|
gopkg.in/ini.v1 v1.38.2 h1:dGcbywv4RufeGeiMycPT/plKB5FtmLKLnWKwBiLhUA4=
|
||||||
|
gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/src-d/go-billy.v4 v4.2.0 h1:VGbrP1EsYxtvVPEiHui+4//imr4E5MGEFLx66bQtusg=
|
||||||
|
gopkg.in/src-d/go-billy.v4 v4.2.0/go.mod h1:ZHSF0JP+7oD97194otDUCD7Ofbk63+xFcfWP5bT6h+Q=
|
||||||
|
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs=
|
||||||
|
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||||
|
gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714 h1:+wM2BGgQ1znCKBexOB4OrGVSDw8mtKNUSq3wqxZhi/k=
|
||||||
|
gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714/go.mod h1:CzbUWqMn4pvmvndg3gnh5iZFmSsbhyhUWdI0IQ60AQo=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||||
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
5
main.go
5
main.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -40,13 +41,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
appConfig, err := config.NewAppConfig("lazygit", version, commit, date, buildSource, debuggingFlag)
|
appConfig, err := config.NewAppConfig("lazygit", version, commit, date, buildSource, debuggingFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
app, err := app.Setup(appConfig)
|
app, err := app.Setup(appConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.Log.Error(err.Error())
|
app.Log.Error(err.Error())
|
||||||
panic(err)
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Gui.RunWithSubprocesses()
|
app.Gui.RunWithSubprocesses()
|
||||||
|
@ -14,9 +14,9 @@ type Branch struct {
|
|||||||
Recency string
|
Recency string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDisplayString returns the dispaly string of branch
|
// GetDisplayStrings returns the dispaly string of branch
|
||||||
func (b *Branch) GetDisplayString() string {
|
func (b *Branch) GetDisplayStrings() []string {
|
||||||
return utils.WithPadding(b.Recency, 4) + utils.ColoredString(b.Name, b.GetColor())
|
return []string{b.Recency, utils.ColoredString(b.Name, b.GetColor())}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetColor branch color
|
// GetColor branch color
|
||||||
|
26
pkg/commands/commit.go
Normal file
26
pkg/commands/commit.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatih/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Commit : A git commit
|
||||||
|
type Commit struct {
|
||||||
|
Sha string
|
||||||
|
Name string
|
||||||
|
Pushed bool
|
||||||
|
DisplayString string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Commit) GetDisplayStrings() []string {
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
yellow := color.New(color.FgYellow)
|
||||||
|
white := color.New(color.FgWhite)
|
||||||
|
|
||||||
|
shaColor := yellow
|
||||||
|
if c.Pushed {
|
||||||
|
shaColor = red
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{shaColor.Sprint(c.Sha), white.Sprint(c.Name)}
|
||||||
|
}
|
36
pkg/commands/file.go
Normal file
36
pkg/commands/file.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import "github.com/fatih/color"
|
||||||
|
|
||||||
|
// File : A file from git status
|
||||||
|
// duplicating this for now
|
||||||
|
type File struct {
|
||||||
|
Name string
|
||||||
|
HasStagedChanges bool
|
||||||
|
HasUnstagedChanges bool
|
||||||
|
Tracked bool
|
||||||
|
Deleted bool
|
||||||
|
HasMergeConflicts bool
|
||||||
|
DisplayString string
|
||||||
|
Type string // one of 'file', 'directory', and 'other'
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDisplayStrings returns the display string of a file
|
||||||
|
func (f *File) GetDisplayStrings() []string {
|
||||||
|
// potentially inefficient to be instantiating these color
|
||||||
|
// objects with each render
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
green := color.New(color.FgGreen)
|
||||||
|
if !f.Tracked && !f.HasStagedChanges {
|
||||||
|
return []string{red.Sprint(f.DisplayString)}
|
||||||
|
}
|
||||||
|
|
||||||
|
output := green.Sprint(f.DisplayString[0:1])
|
||||||
|
output += red.Sprint(f.DisplayString[1:3])
|
||||||
|
if f.HasUnstagedChanges {
|
||||||
|
output += red.Sprint(f.Name)
|
||||||
|
} else {
|
||||||
|
output += green.Sprint(f.Name)
|
||||||
|
}
|
||||||
|
return []string{output}
|
||||||
|
}
|
@ -7,7 +7,6 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -64,6 +63,8 @@ type GitCommand struct {
|
|||||||
Worktree *gogit.Worktree
|
Worktree *gogit.Worktree
|
||||||
Repo *gogit.Repository
|
Repo *gogit.Repository
|
||||||
Tr *i18n.Localizer
|
Tr *i18n.Localizer
|
||||||
|
getGlobalGitConfig func(string) (string, error)
|
||||||
|
getLocalGitConfig func(string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGitCommand it runs git commands
|
// NewGitCommand it runs git commands
|
||||||
@ -97,21 +98,23 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer)
|
|||||||
Tr: tr,
|
Tr: tr,
|
||||||
Worktree: worktree,
|
Worktree: worktree,
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
|
getGlobalGitConfig: gitconfig.Global,
|
||||||
|
getLocalGitConfig: gitconfig.Local,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStashEntries stash entryies
|
// GetStashEntries stash entryies
|
||||||
func (c *GitCommand) GetStashEntries() []StashEntry {
|
func (c *GitCommand) GetStashEntries() []*StashEntry {
|
||||||
rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'")
|
rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'")
|
||||||
stashEntries := []StashEntry{}
|
stashEntries := []*StashEntry{}
|
||||||
for i, line := range utils.SplitLines(rawString) {
|
for i, line := range utils.SplitLines(rawString) {
|
||||||
stashEntries = append(stashEntries, stashEntryFromLine(line, i))
|
stashEntries = append(stashEntries, stashEntryFromLine(line, i))
|
||||||
}
|
}
|
||||||
return stashEntries
|
return stashEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
func stashEntryFromLine(line string, index int) StashEntry {
|
func stashEntryFromLine(line string, index int) *StashEntry {
|
||||||
return StashEntry{
|
return &StashEntry{
|
||||||
Name: line,
|
Name: line,
|
||||||
Index: index,
|
Index: index,
|
||||||
DisplayString: line,
|
DisplayString: line,
|
||||||
@ -123,33 +126,26 @@ func (c *GitCommand) GetStashEntryDiff(index int) (string, error) {
|
|||||||
return c.OSCommand.RunCommandWithOutput("git stash show -p --color stash@{" + fmt.Sprint(index) + "}")
|
return c.OSCommand.RunCommandWithOutput("git stash show -p --color stash@{" + fmt.Sprint(index) + "}")
|
||||||
}
|
}
|
||||||
|
|
||||||
func includes(array []string, str string) bool {
|
|
||||||
for _, arrayStr := range array {
|
|
||||||
if arrayStr == str {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStatusFiles git status files
|
// GetStatusFiles git status files
|
||||||
func (c *GitCommand) GetStatusFiles() []File {
|
func (c *GitCommand) GetStatusFiles() []*File {
|
||||||
statusOutput, _ := c.GitStatus()
|
statusOutput, _ := c.GitStatus()
|
||||||
statusStrings := utils.SplitLines(statusOutput)
|
statusStrings := utils.SplitLines(statusOutput)
|
||||||
files := []File{}
|
files := []*File{}
|
||||||
|
|
||||||
for _, statusString := range statusStrings {
|
for _, statusString := range statusStrings {
|
||||||
change := statusString[0:2]
|
change := statusString[0:2]
|
||||||
stagedChange := change[0:1]
|
stagedChange := change[0:1]
|
||||||
unstagedChange := statusString[1:2]
|
unstagedChange := statusString[1:2]
|
||||||
filename := c.OSCommand.Unquote(statusString[3:])
|
filename := c.OSCommand.Unquote(statusString[3:])
|
||||||
tracked := !includes([]string{"??", "A ", "AM"}, change)
|
_, untracked := map[string]bool{"??": true, "A ": true, "AM": true}[change]
|
||||||
file := File{
|
_, hasNoStagedChanges := map[string]bool{" ": true, "U": true, "?": true}[stagedChange]
|
||||||
|
|
||||||
|
file := &File{
|
||||||
Name: filename,
|
Name: filename,
|
||||||
DisplayString: statusString,
|
DisplayString: statusString,
|
||||||
HasStagedChanges: !includes([]string{" ", "U", "?"}, stagedChange),
|
HasStagedChanges: !hasNoStagedChanges,
|
||||||
HasUnstagedChanges: unstagedChange != " ",
|
HasUnstagedChanges: unstagedChange != " ",
|
||||||
Tracked: tracked,
|
Tracked: !untracked,
|
||||||
Deleted: unstagedChange == "D" || stagedChange == "D",
|
Deleted: unstagedChange == "D" || stagedChange == "D",
|
||||||
HasMergeConflicts: change == "UU",
|
HasMergeConflicts: change == "UU",
|
||||||
Type: c.OSCommand.FileType(filename),
|
Type: c.OSCommand.FileType(filename),
|
||||||
@ -172,33 +168,42 @@ func (c *GitCommand) StashSave(message string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MergeStatusFiles merge status files
|
// MergeStatusFiles merge status files
|
||||||
func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []File) []File {
|
func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []*File) []*File {
|
||||||
if len(oldFiles) == 0 {
|
if len(oldFiles) == 0 {
|
||||||
return newFiles
|
return newFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
headResults := []File{}
|
appendedIndexes := []int{}
|
||||||
tailResults := []File{}
|
|
||||||
|
|
||||||
for _, newFile := range newFiles {
|
|
||||||
var isHeadResult bool
|
|
||||||
|
|
||||||
|
// retain position of files we already could see
|
||||||
|
result := []*File{}
|
||||||
for _, oldFile := range oldFiles {
|
for _, oldFile := range oldFiles {
|
||||||
|
for newIndex, newFile := range newFiles {
|
||||||
if oldFile.Name == newFile.Name {
|
if oldFile.Name == newFile.Name {
|
||||||
isHeadResult = true
|
result = append(result, newFile)
|
||||||
|
appendedIndexes = append(appendedIndexes, newIndex)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isHeadResult {
|
|
||||||
headResults = append(headResults, newFile)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tailResults = append(tailResults, newFile)
|
// append any new files to the end
|
||||||
|
for index, newFile := range newFiles {
|
||||||
|
if !includesInt(appendedIndexes, index) {
|
||||||
|
result = append(result, newFile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(headResults, tailResults...)
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func includesInt(list []int, a int) bool {
|
||||||
|
for _, b := range list {
|
||||||
|
if b == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBranchName branch name
|
// GetBranchName branch name
|
||||||
@ -230,14 +235,14 @@ func (c *GitCommand) UpstreamDifferenceCount() (string, string) {
|
|||||||
func (c *GitCommand) GetCommitsToPush() []string {
|
func (c *GitCommand) GetCommitsToPush() []string {
|
||||||
pushables, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --abbrev-commit")
|
pushables, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --abbrev-commit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return make([]string, 0)
|
return []string{}
|
||||||
}
|
}
|
||||||
return utils.SplitLines(pushables)
|
return utils.SplitLines(pushables)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenameCommit renames the topmost commit with the given name
|
// RenameCommit renames the topmost commit with the given name
|
||||||
func (c *GitCommand) RenameCommit(name string) error {
|
func (c *GitCommand) RenameCommit(name string) error {
|
||||||
return c.OSCommand.RunCommand("git commit --allow-empty --amend -m " + c.OSCommand.Quote(name))
|
return c.OSCommand.RunCommand(fmt.Sprintf("git commit --allow-empty --amend -m %s", c.OSCommand.Quote(name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch fetch git repo
|
// Fetch fetch git repo
|
||||||
@ -247,23 +252,23 @@ func (c *GitCommand) Fetch() error {
|
|||||||
|
|
||||||
// ResetToCommit reset to commit
|
// ResetToCommit reset to commit
|
||||||
func (c *GitCommand) ResetToCommit(sha string) error {
|
func (c *GitCommand) ResetToCommit(sha string) error {
|
||||||
return c.OSCommand.RunCommand("git reset " + sha)
|
return c.OSCommand.RunCommand(fmt.Sprintf("git reset %s", sha))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBranch create new branch
|
// NewBranch create new branch
|
||||||
func (c *GitCommand) NewBranch(name string) error {
|
func (c *GitCommand) NewBranch(name string) error {
|
||||||
return c.OSCommand.RunCommand("git checkout -b " + name)
|
return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -b %s", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBranch delete branch
|
// DeleteBranch delete branch
|
||||||
func (c *GitCommand) DeleteBranch(branch string, force bool) error {
|
func (c *GitCommand) DeleteBranch(branch string, force bool) error {
|
||||||
var command string
|
command := "git branch -d"
|
||||||
|
|
||||||
if force {
|
if force {
|
||||||
command = "git branch -D"
|
command = "git branch -D"
|
||||||
} else {
|
|
||||||
command = "git branch -d "
|
|
||||||
}
|
}
|
||||||
return c.OSCommand.RunCommand(command + branch)
|
|
||||||
|
return c.OSCommand.RunCommand(fmt.Sprintf("%s %s", command, branch))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListStash list stash
|
// ListStash list stash
|
||||||
@ -273,7 +278,7 @@ func (c *GitCommand) ListStash() (string, error) {
|
|||||||
|
|
||||||
// Merge merge
|
// Merge merge
|
||||||
func (c *GitCommand) Merge(branchName string) error {
|
func (c *GitCommand) Merge(branchName string) error {
|
||||||
return c.OSCommand.RunCommand("git merge --no-edit " + branchName)
|
return c.OSCommand.RunCommand(fmt.Sprintf("git merge --no-edit %s", branchName))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbortMerge abort merge
|
// AbortMerge abort merge
|
||||||
@ -281,96 +286,91 @@ func (c *GitCommand) AbortMerge() error {
|
|||||||
return c.OSCommand.RunCommand("git merge --abort")
|
return c.OSCommand.RunCommand("git merge --abort")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsingGpg tells us whether the user has gpg enabled so that we can know
|
// usingGpg tells us whether the user has gpg enabled so that we can know
|
||||||
// whether we need to run a subprocess to allow them to enter their password
|
// whether we need to run a subprocess to allow them to enter their password
|
||||||
func (c *GitCommand) UsingGpg() bool {
|
func (c *GitCommand) usingGpg() bool {
|
||||||
gpgsign, _ := gitconfig.Global("commit.gpgsign")
|
gpgsign, _ := c.getLocalGitConfig("commit.gpgsign")
|
||||||
if gpgsign == "" {
|
if gpgsign == "" {
|
||||||
gpgsign, _ = gitconfig.Local("commit.gpgsign")
|
gpgsign, _ = c.getGlobalGitConfig("commit.gpgsign")
|
||||||
}
|
}
|
||||||
if gpgsign == "" {
|
value := strings.ToLower(gpgsign)
|
||||||
return false
|
|
||||||
}
|
return value == "true" || value == "1" || value == "yes" || value == "on"
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit commit to git
|
// Commit commits to git
|
||||||
func (c *GitCommand) Commit(g *gocui.Gui, message string) (*exec.Cmd, error) {
|
func (c *GitCommand) Commit(message string) (*exec.Cmd, error) {
|
||||||
command := "git commit -m " + c.OSCommand.Quote(message)
|
command := fmt.Sprintf("git commit -m %s", c.OSCommand.Quote(message))
|
||||||
if c.UsingGpg() {
|
if c.usingGpg() {
|
||||||
return c.OSCommand.PrepareSubProcess(c.OSCommand.Platform.shell, c.OSCommand.Platform.shellArg, command), nil
|
return c.OSCommand.PrepareSubProcess(c.OSCommand.Platform.shell, c.OSCommand.Platform.shellArg, command), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, c.OSCommand.RunCommand(command)
|
return nil, c.OSCommand.RunCommand(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull pull from repo
|
// Pull pulls from repo
|
||||||
func (c *GitCommand) Pull() error {
|
func (c *GitCommand) Pull() error {
|
||||||
return c.OSCommand.RunCommand("git pull --no-edit")
|
return c.OSCommand.RunCommand("git pull --no-edit")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push push to a branch
|
// Push pushes to a branch
|
||||||
func (c *GitCommand) Push(branchName string, force bool) error {
|
func (c *GitCommand) Push(branchName string, force bool) error {
|
||||||
forceFlag := ""
|
forceFlag := ""
|
||||||
if force {
|
if force {
|
||||||
forceFlag = "--force-with-lease "
|
forceFlag = "--force-with-lease "
|
||||||
}
|
}
|
||||||
return c.OSCommand.RunCommand("git push " + forceFlag + "-u origin " + branchName)
|
|
||||||
|
return c.OSCommand.RunCommand(fmt.Sprintf("git push %s -u origin %s", forceFlag, branchName))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SquashPreviousTwoCommits squashes a commit down to the one below it
|
// SquashPreviousTwoCommits squashes a commit down to the one below it
|
||||||
// retaining the message of the higher commit
|
// retaining the message of the higher commit
|
||||||
func (c *GitCommand) SquashPreviousTwoCommits(message string) error {
|
func (c *GitCommand) SquashPreviousTwoCommits(message string) error {
|
||||||
// TODO: test this
|
// TODO: test this
|
||||||
err := c.OSCommand.RunCommand("git reset --soft HEAD^")
|
if err := c.OSCommand.RunCommand("git reset --soft HEAD^"); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO: if password is required, we need to return a subprocess
|
// TODO: if password is required, we need to return a subprocess
|
||||||
return c.OSCommand.RunCommand("git commit --amend -m " + c.OSCommand.Quote(message))
|
return c.OSCommand.RunCommand(fmt.Sprintf("git commit --amend -m %s", c.OSCommand.Quote(message)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SquashFixupCommit squashes a 'FIXUP' commit into the commit beneath it,
|
// SquashFixupCommit squashes a 'FIXUP' commit into the commit beneath it,
|
||||||
// retaining the commit message of the lower commit
|
// retaining the commit message of the lower commit
|
||||||
func (c *GitCommand) SquashFixupCommit(branchName string, shaValue string) error {
|
func (c *GitCommand) SquashFixupCommit(branchName string, shaValue string) error {
|
||||||
var err error
|
|
||||||
commands := []string{
|
commands := []string{
|
||||||
"git checkout -q " + shaValue,
|
fmt.Sprintf("git checkout -q %s", shaValue),
|
||||||
"git reset --soft " + shaValue + "^",
|
fmt.Sprintf("git reset --soft %s^", shaValue),
|
||||||
"git commit --amend -C " + shaValue + "^",
|
fmt.Sprintf("git commit --amend -C %s^", shaValue),
|
||||||
"git rebase --onto HEAD " + shaValue + " " + branchName,
|
fmt.Sprintf("git rebase --onto HEAD %s %s", shaValue, branchName),
|
||||||
}
|
}
|
||||||
ret := ""
|
|
||||||
for _, command := range commands {
|
for _, command := range commands {
|
||||||
c.Log.Info(command)
|
c.Log.Info(command)
|
||||||
output, err := c.OSCommand.RunCommandWithOutput(command)
|
|
||||||
ret += output
|
if output, err := c.OSCommand.RunCommandWithOutput(command); err != nil {
|
||||||
if err != nil {
|
ret := output
|
||||||
c.Log.Info(ret)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// We are already in an error state here so we're just going to append
|
// We are already in an error state here so we're just going to append
|
||||||
// the output of these commands
|
// the output of these commands
|
||||||
output, _ := c.OSCommand.RunCommandWithOutput("git branch -d " + shaValue)
|
output, _ := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git branch -d %s", shaValue))
|
||||||
ret += output
|
ret += output
|
||||||
output, _ = c.OSCommand.RunCommandWithOutput("git checkout " + branchName)
|
output, _ = c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git checkout %s", branchName))
|
||||||
ret += output
|
ret += output
|
||||||
}
|
|
||||||
if err != nil {
|
c.Log.Info(ret)
|
||||||
return errors.New(ret)
|
return errors.New(ret)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CatFile obtain the contents of a file
|
// CatFile obtains the content of a file
|
||||||
func (c *GitCommand) CatFile(fileName string) (string, error) {
|
func (c *GitCommand) CatFile(fileName string) (string, error) {
|
||||||
return c.OSCommand.RunCommandWithOutput("cat " + c.OSCommand.Quote(fileName))
|
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("cat %s", c.OSCommand.Quote(fileName)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// StageFile stages a file
|
// StageFile stages a file
|
||||||
func (c *GitCommand) StageFile(fileName string) error {
|
func (c *GitCommand) StageFile(fileName string) error {
|
||||||
return c.OSCommand.RunCommand("git add " + c.OSCommand.Quote(fileName))
|
return c.OSCommand.RunCommand(fmt.Sprintf("git add %s", c.OSCommand.Quote(fileName)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// StageAll stages all files
|
// StageAll stages all files
|
||||||
@ -385,13 +385,11 @@ func (c *GitCommand) UnstageAll() error {
|
|||||||
|
|
||||||
// UnStageFile unstages a file
|
// UnStageFile unstages a file
|
||||||
func (c *GitCommand) UnStageFile(fileName string, tracked bool) error {
|
func (c *GitCommand) UnStageFile(fileName string, tracked bool) error {
|
||||||
var command string
|
command := "git rm --cached %s"
|
||||||
if tracked {
|
if tracked {
|
||||||
command = "git reset HEAD "
|
command = "git reset HEAD %s"
|
||||||
} else {
|
|
||||||
command = "git rm --cached "
|
|
||||||
}
|
}
|
||||||
return c.OSCommand.RunCommand(command + c.OSCommand.Quote(fileName))
|
return c.OSCommand.RunCommand(fmt.Sprintf(command, c.OSCommand.Quote(fileName)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitStatus returns the plaintext short status of the repo
|
// GitStatus returns the plaintext short status of the repo
|
||||||
@ -409,7 +407,7 @@ func (c *GitCommand) IsInMergeState() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveFile directly
|
// RemoveFile directly
|
||||||
func (c *GitCommand) RemoveFile(file File) error {
|
func (c *GitCommand) RemoveFile(file *File) error {
|
||||||
// if the file isn't tracked, we assume you want to delete it
|
// if the file isn't tracked, we assume you want to delete it
|
||||||
if file.HasStagedChanges {
|
if file.HasStagedChanges {
|
||||||
if err := c.OSCommand.RunCommand("git reset -- " + file.Name); err != nil {
|
if err := c.OSCommand.RunCommand("git reset -- " + file.Name); err != nil {
|
||||||
@ -465,17 +463,17 @@ func includesString(list []string, a string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCommits obtains the commits of the current branch
|
// GetCommits obtains the commits of the current branch
|
||||||
func (c *GitCommand) GetCommits() []Commit {
|
func (c *GitCommand) GetCommits() []*Commit {
|
||||||
pushables := c.GetCommitsToPush()
|
pushables := c.GetCommitsToPush()
|
||||||
log := c.GetLog()
|
log := c.GetLog()
|
||||||
commits := make([]Commit, 0)
|
commits := []*Commit{}
|
||||||
// now we can split it up and turn it into commits
|
// now we can split it up and turn it into commits
|
||||||
lines := utils.SplitLines(log)
|
lines := utils.SplitLines(log)
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
splitLine := strings.Split(line, " ")
|
splitLine := strings.Split(line, " ")
|
||||||
sha := splitLine[0]
|
sha := splitLine[0]
|
||||||
pushed := includesString(pushables, sha)
|
pushed := includesString(pushables, sha)
|
||||||
commits = append(commits, Commit{
|
commits = append(commits, &Commit{
|
||||||
Sha: sha,
|
Sha: sha,
|
||||||
Name: strings.Join(splitLine[1:], " "),
|
Name: strings.Join(splitLine[1:], " "),
|
||||||
Pushed: pushed,
|
Pushed: pushed,
|
||||||
@ -513,7 +511,7 @@ func (c *GitCommand) Show(sha string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Diff returns the diff of a file
|
// Diff returns the diff of a file
|
||||||
func (c *GitCommand) Diff(file File) string {
|
func (c *GitCommand) Diff(file *File) string {
|
||||||
cachedArg := ""
|
cachedArg := ""
|
||||||
fileName := c.OSCommand.Quote(file.Name)
|
fileName := c.OSCommand.Quote(file.Name)
|
||||||
if file.HasStagedChanges && !file.HasUnstagedChanges {
|
if file.HasStagedChanges && !file.HasUnstagedChanges {
|
||||||
|
@ -1,32 +1,5 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
// File : A staged/unstaged file
|
|
||||||
type File struct {
|
|
||||||
Name string
|
|
||||||
HasStagedChanges bool
|
|
||||||
HasUnstagedChanges bool
|
|
||||||
Tracked bool
|
|
||||||
Deleted bool
|
|
||||||
HasMergeConflicts bool
|
|
||||||
DisplayString string
|
|
||||||
Type string // one of 'file', 'directory', and 'other'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit : A git commit
|
|
||||||
type Commit struct {
|
|
||||||
Sha string
|
|
||||||
Name string
|
|
||||||
Pushed bool
|
|
||||||
DisplayString string
|
|
||||||
}
|
|
||||||
|
|
||||||
// StashEntry : A git stash entry
|
|
||||||
type StashEntry struct {
|
|
||||||
Index int
|
|
||||||
Name string
|
|
||||||
DisplayString string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conflict : A git conflict with a start middle and end corresponding to line
|
// Conflict : A git conflict with a start middle and end corresponding to line
|
||||||
// numbers in the file where the conflict bars appear
|
// numbers in the file where the conflict bars appear
|
||||||
type Conflict struct {
|
type Conflict struct {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,7 @@ type Platform struct {
|
|||||||
shellArg string
|
shellArg string
|
||||||
escapedQuote string
|
escapedQuote string
|
||||||
openCommand string
|
openCommand string
|
||||||
|
fallbackEscapedQuote string
|
||||||
}
|
}
|
||||||
|
|
||||||
// OSCommand holds all the os commands
|
// OSCommand holds all the os commands
|
||||||
@ -140,7 +141,11 @@ func (c *OSCommand) PrepareSubProcess(cmdName string, commandArgs ...string) *ex
|
|||||||
// Quote wraps a message in platform-specific quotation marks
|
// Quote wraps a message in platform-specific quotation marks
|
||||||
func (c *OSCommand) Quote(message string) string {
|
func (c *OSCommand) Quote(message string) string {
|
||||||
message = strings.Replace(message, "`", "\\`", -1)
|
message = strings.Replace(message, "`", "\\`", -1)
|
||||||
return c.Platform.escapedQuote + message + c.Platform.escapedQuote
|
escapedQuote := c.Platform.escapedQuote
|
||||||
|
if strings.Contains(message, c.Platform.escapedQuote) {
|
||||||
|
escapedQuote = c.Platform.fallbackEscapedQuote
|
||||||
|
}
|
||||||
|
return escapedQuote + message + escapedQuote
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unquote removes wrapping quotations marks if they are present
|
// Unquote removes wrapping quotations marks if they are present
|
||||||
|
@ -11,7 +11,8 @@ func getPlatform() *Platform {
|
|||||||
os: runtime.GOOS,
|
os: runtime.GOOS,
|
||||||
shell: "bash",
|
shell: "bash",
|
||||||
shellArg: "-c",
|
shellArg: "-c",
|
||||||
escapedQuote: "\"",
|
escapedQuote: "'",
|
||||||
openCommand: "open {{filename}}",
|
openCommand: "open {{filename}}",
|
||||||
|
fallbackEscapedQuote: "\"",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,6 +265,32 @@ func TestOSCommandQuote(t *testing.T) {
|
|||||||
assert.EqualValues(t, expected, actual)
|
assert.EqualValues(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestOSCommandQuoteSingleQuote tests the quote function with ' quotes explicitly for Linux
|
||||||
|
func TestOSCommandQuoteSingleQuote(t *testing.T) {
|
||||||
|
osCommand := newDummyOSCommand()
|
||||||
|
|
||||||
|
osCommand.Platform.os = "linux"
|
||||||
|
|
||||||
|
actual := osCommand.Quote("hello 'test'")
|
||||||
|
|
||||||
|
expected := osCommand.Platform.fallbackEscapedQuote + "hello 'test'" + osCommand.Platform.fallbackEscapedQuote
|
||||||
|
|
||||||
|
assert.EqualValues(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOSCommandQuoteSingleQuote tests the quote function with " quotes explicitly for Linux
|
||||||
|
func TestOSCommandQuoteDoubleQuote(t *testing.T) {
|
||||||
|
osCommand := newDummyOSCommand()
|
||||||
|
|
||||||
|
osCommand.Platform.os = "linux"
|
||||||
|
|
||||||
|
actual := osCommand.Quote(`hello "test"`)
|
||||||
|
|
||||||
|
expected := osCommand.Platform.escapedQuote + "hello \"test\"" + osCommand.Platform.escapedQuote
|
||||||
|
|
||||||
|
assert.EqualValues(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
func TestOSCommandUnquote(t *testing.T) {
|
func TestOSCommandUnquote(t *testing.T) {
|
||||||
osCommand := newDummyOSCommand()
|
osCommand := newDummyOSCommand()
|
||||||
|
|
||||||
|
@ -6,5 +6,6 @@ func getPlatform() *Platform {
|
|||||||
shell: "cmd",
|
shell: "cmd",
|
||||||
shellArg: "/c",
|
shellArg: "/c",
|
||||||
escapedQuote: `\"`,
|
escapedQuote: `\"`,
|
||||||
|
fallbackEscapedQuote: "\\'",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
pkg/commands/stash_entry.go
Normal file
13
pkg/commands/stash_entry.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
// StashEntry : A git stash entry
|
||||||
|
type StashEntry struct {
|
||||||
|
Index int
|
||||||
|
Name string
|
||||||
|
DisplayString string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDisplayStrings returns the dispaly string of branch
|
||||||
|
func (s *StashEntry) GetDisplayStrings() []string {
|
||||||
|
return []string{s.DisplayString}
|
||||||
|
}
|
@ -34,7 +34,7 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand) (*
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BranchListBuilder) obtainCurrentBranch() commands.Branch {
|
func (b *BranchListBuilder) obtainCurrentBranch() *commands.Branch {
|
||||||
// I used go-git for this, but that breaks if you've just done a git init,
|
// I used go-git for this, but that breaks if you've just done a git init,
|
||||||
// even though you're on 'master'
|
// even though you're on 'master'
|
||||||
branchName, err := b.GitCommand.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD")
|
branchName, err := b.GitCommand.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD")
|
||||||
@ -44,11 +44,11 @@ func (b *BranchListBuilder) obtainCurrentBranch() commands.Branch {
|
|||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return commands.Branch{Name: strings.TrimSpace(branchName), Recency: " *"}
|
return &commands.Branch{Name: strings.TrimSpace(branchName), Recency: " *"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BranchListBuilder) obtainReflogBranches() []commands.Branch {
|
func (b *BranchListBuilder) obtainReflogBranches() []*commands.Branch {
|
||||||
branches := make([]commands.Branch, 0)
|
branches := make([]*commands.Branch, 0)
|
||||||
rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput("git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD")
|
rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput("git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return branches
|
return branches
|
||||||
@ -58,14 +58,14 @@ func (b *BranchListBuilder) obtainReflogBranches() []commands.Branch {
|
|||||||
for _, line := range branchLines {
|
for _, line := range branchLines {
|
||||||
timeNumber, timeUnit, branchName := branchInfoFromLine(line)
|
timeNumber, timeUnit, branchName := branchInfoFromLine(line)
|
||||||
timeUnit = abbreviatedTimeUnit(timeUnit)
|
timeUnit = abbreviatedTimeUnit(timeUnit)
|
||||||
branch := commands.Branch{Name: branchName, Recency: timeNumber + timeUnit}
|
branch := &commands.Branch{Name: branchName, Recency: timeNumber + timeUnit}
|
||||||
branches = append(branches, branch)
|
branches = append(branches, branch)
|
||||||
}
|
}
|
||||||
return branches
|
return branches
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BranchListBuilder) obtainSafeBranches() []commands.Branch {
|
func (b *BranchListBuilder) obtainSafeBranches() []*commands.Branch {
|
||||||
branches := make([]commands.Branch, 0)
|
branches := make([]*commands.Branch, 0)
|
||||||
|
|
||||||
bIter, err := b.GitCommand.Repo.Branches()
|
bIter, err := b.GitCommand.Repo.Branches()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -73,14 +73,14 @@ func (b *BranchListBuilder) obtainSafeBranches() []commands.Branch {
|
|||||||
}
|
}
|
||||||
err = bIter.ForEach(func(b *plumbing.Reference) error {
|
err = bIter.ForEach(func(b *plumbing.Reference) error {
|
||||||
name := b.Name().Short()
|
name := b.Name().Short()
|
||||||
branches = append(branches, commands.Branch{Name: name})
|
branches = append(branches, &commands.Branch{Name: name})
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return branches
|
return branches
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existingBranches []commands.Branch, included bool) []commands.Branch {
|
func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existingBranches []*commands.Branch, included bool) []*commands.Branch {
|
||||||
for _, newBranch := range newBranches {
|
for _, newBranch := range newBranches {
|
||||||
if included == branchIncluded(newBranch.Name, existingBranches) {
|
if included == branchIncluded(newBranch.Name, existingBranches) {
|
||||||
finalBranches = append(finalBranches, newBranch)
|
finalBranches = append(finalBranches, newBranch)
|
||||||
@ -89,7 +89,7 @@ func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existi
|
|||||||
return finalBranches
|
return finalBranches
|
||||||
}
|
}
|
||||||
|
|
||||||
func sanitisedReflogName(reflogBranch commands.Branch, safeBranches []commands.Branch) string {
|
func sanitisedReflogName(reflogBranch *commands.Branch, safeBranches []*commands.Branch) string {
|
||||||
for _, safeBranch := range safeBranches {
|
for _, safeBranch := range safeBranches {
|
||||||
if strings.ToLower(safeBranch.Name) == strings.ToLower(reflogBranch.Name) {
|
if strings.ToLower(safeBranch.Name) == strings.ToLower(reflogBranch.Name) {
|
||||||
return safeBranch.Name
|
return safeBranch.Name
|
||||||
@ -99,15 +99,15 @@ func sanitisedReflogName(reflogBranch commands.Branch, safeBranches []commands.B
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the list of branches for the current repo
|
// Build the list of branches for the current repo
|
||||||
func (b *BranchListBuilder) Build() []commands.Branch {
|
func (b *BranchListBuilder) Build() []*commands.Branch {
|
||||||
branches := make([]commands.Branch, 0)
|
branches := make([]*commands.Branch, 0)
|
||||||
head := b.obtainCurrentBranch()
|
head := b.obtainCurrentBranch()
|
||||||
safeBranches := b.obtainSafeBranches()
|
safeBranches := b.obtainSafeBranches()
|
||||||
if len(safeBranches) == 0 {
|
if len(safeBranches) == 0 {
|
||||||
return append(branches, head)
|
return append(branches, head)
|
||||||
}
|
}
|
||||||
reflogBranches := b.obtainReflogBranches()
|
reflogBranches := b.obtainReflogBranches()
|
||||||
reflogBranches = uniqueByName(append([]commands.Branch{head}, reflogBranches...))
|
reflogBranches = uniqueByName(append([]*commands.Branch{head}, reflogBranches...))
|
||||||
for i, reflogBranch := range reflogBranches {
|
for i, reflogBranch := range reflogBranches {
|
||||||
reflogBranches[i].Name = sanitisedReflogName(reflogBranch, safeBranches)
|
reflogBranches[i].Name = sanitisedReflogName(reflogBranch, safeBranches)
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ func (b *BranchListBuilder) Build() []commands.Branch {
|
|||||||
return branches
|
return branches
|
||||||
}
|
}
|
||||||
|
|
||||||
func branchIncluded(branchName string, branches []commands.Branch) bool {
|
func branchIncluded(branchName string, branches []*commands.Branch) bool {
|
||||||
for _, existingBranch := range branches {
|
for _, existingBranch := range branches {
|
||||||
if strings.ToLower(existingBranch.Name) == strings.ToLower(branchName) {
|
if strings.ToLower(existingBranch.Name) == strings.ToLower(branchName) {
|
||||||
return true
|
return true
|
||||||
@ -127,8 +127,8 @@ func branchIncluded(branchName string, branches []commands.Branch) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func uniqueByName(branches []commands.Branch) []commands.Branch {
|
func uniqueByName(branches []*commands.Branch) []*commands.Branch {
|
||||||
finalBranches := make([]commands.Branch, 0)
|
finalBranches := make([]*commands.Branch, 0)
|
||||||
for _, branch := range branches {
|
for _, branch := range branches {
|
||||||
if branchIncluded(branch.Name, finalBranches) {
|
if branchIncluded(branch.Name, finalBranches) {
|
||||||
continue
|
continue
|
||||||
|
@ -7,14 +7,15 @@ import (
|
|||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/git"
|
"github.com/jesseduffield/lazygit/pkg/git"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
||||||
index := gui.getItemPosition(v)
|
index := gui.getItemPosition(gui.getBranchesView(g))
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
|
||||||
}
|
}
|
||||||
branch := gui.getSelectedBranch(v)
|
branch := gui.getSelectedBranch(gui.getBranchesView(g))
|
||||||
if err := gui.GitCommand.Checkout(branch.Name, false); err != nil {
|
if err := gui.GitCommand.Checkout(branch.Name, false); err != nil {
|
||||||
gui.createErrorPanel(g, err.Error())
|
gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
@ -109,22 +110,13 @@ func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSelectedBranch(v *gocui.View) commands.Branch {
|
func (gui *Gui) getSelectedBranch(v *gocui.View) *commands.Branch {
|
||||||
lineNumber := gui.getItemPosition(v)
|
lineNumber := gui.getItemPosition(v)
|
||||||
return gui.State.Branches[lineNumber]
|
return gui.State.Branches[lineNumber]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderBranchesOptions(g *gocui.Gui) error {
|
func (gui *Gui) renderBranchesOptions(g *gocui.Gui) error {
|
||||||
return gui.renderOptionsMap(g, map[string]string{
|
return gui.renderGlobalOptions(g)
|
||||||
"space": gui.Tr.SLocalize("checkout"),
|
|
||||||
"f": gui.Tr.SLocalize("forceCheckout"),
|
|
||||||
"m": gui.Tr.SLocalize("merge"),
|
|
||||||
"c": gui.Tr.SLocalize("checkoutByName"),
|
|
||||||
"n": gui.Tr.SLocalize("newBranch"),
|
|
||||||
"d": gui.Tr.SLocalize("deleteBranch"),
|
|
||||||
"D": gui.Tr.SLocalize("forceDeleteBranch"),
|
|
||||||
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// may want to standardise how these select methods work
|
// may want to standardise how these select methods work
|
||||||
@ -160,10 +152,15 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gui.State.Branches = builder.Build()
|
gui.State.Branches = builder.Build()
|
||||||
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
for _, branch := range gui.State.Branches {
|
list, err := utils.RenderList(gui.State.Branches)
|
||||||
fmt.Fprintln(v, branch.GetDisplayString())
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(v, list)
|
||||||
|
|
||||||
gui.resetOrigin(v)
|
gui.resetOrigin(v)
|
||||||
return gui.refreshStatus(g)
|
return gui.refreshStatus(g)
|
||||||
})
|
})
|
||||||
|
@ -12,7 +12,7 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if message == "" {
|
if message == "" {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("CommitWithoutMessageErr"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("CommitWithoutMessageErr"))
|
||||||
}
|
}
|
||||||
sub, err := gui.GitCommand.Commit(g, message)
|
sub, err := gui.GitCommand.Commit(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO need to find a way to send through this error
|
// TODO need to find a way to send through this error
|
||||||
if err != gui.Errors.ErrSubProcess {
|
if err != gui.Errors.ErrSubProcess {
|
||||||
|
@ -2,10 +2,11 @@ package gui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
||||||
@ -15,20 +16,14 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
red := color.New(color.FgRed)
|
list, err := utils.RenderList(gui.State.Commits)
|
||||||
yellow := color.New(color.FgYellow)
|
if err != nil {
|
||||||
white := color.New(color.FgWhite)
|
return err
|
||||||
shaColor := white
|
|
||||||
for _, commit := range gui.State.Commits {
|
|
||||||
if commit.Pushed {
|
|
||||||
shaColor = red
|
|
||||||
} else {
|
|
||||||
shaColor = yellow
|
|
||||||
}
|
|
||||||
shaColor.Fprint(v, commit.Sha+" ")
|
|
||||||
white.Fprintln(v, commit.Name)
|
|
||||||
}
|
}
|
||||||
|
fmt.Fprint(v, list)
|
||||||
|
|
||||||
gui.refreshStatus(g)
|
gui.refreshStatus(g)
|
||||||
if g.CurrentView().Name() == "commits" {
|
if g.CurrentView().Name() == "commits" {
|
||||||
gui.handleCommitSelect(g, v)
|
gui.handleCommitSelect(g, v)
|
||||||
@ -59,13 +54,7 @@ func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderCommitsOptions(g *gocui.Gui) error {
|
func (gui *Gui) renderCommitsOptions(g *gocui.Gui) error {
|
||||||
return gui.renderOptionsMap(g, map[string]string{
|
return gui.renderGlobalOptions(g)
|
||||||
"s": gui.Tr.SLocalize("squashDown"),
|
|
||||||
"r": gui.Tr.SLocalize("rename"),
|
|
||||||
"g": gui.Tr.SLocalize("resetToThisCommit"),
|
|
||||||
"f": gui.Tr.SLocalize("fixupCommit"),
|
|
||||||
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -105,7 +94,7 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to files panel
|
// TODO: move to files panel
|
||||||
func (gui *Gui) anyUnStagedChanges(files []commands.File) bool {
|
func (gui *Gui) anyUnStagedChanges(files []*commands.File) bool {
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if file.Tracked && file.HasUnstagedChanges {
|
if file.Tracked && file.HasUnstagedChanges {
|
||||||
return true
|
return true
|
||||||
@ -140,10 +129,10 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.getItemPosition(v) != 0 {
|
if gui.getItemPosition(gui.getCommitsView(g)) != 0 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
||||||
}
|
}
|
||||||
gui.createPromptPanel(g, v, gui.Tr.SLocalize("RenameCommit"), func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("renameCommit"), func(g *gocui.Gui, v *gocui.View) error {
|
||||||
if err := gui.GitCommand.RenameCommit(v.Buffer()); err != nil {
|
if err := gui.GitCommand.RenameCommit(v.Buffer()); err != nil {
|
||||||
return gui.createErrorPanel(g, err.Error())
|
return gui.createErrorPanel(g, err.Error())
|
||||||
}
|
}
|
||||||
@ -156,7 +145,7 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
|
||||||
if gui.getItemPosition(v) != 0 {
|
if gui.getItemPosition(gui.getCommitsView(g)) != 0 {
|
||||||
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,13 +157,13 @@ func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSelectedCommit(g *gocui.Gui) (commands.Commit, error) {
|
func (gui *Gui) getSelectedCommit(g *gocui.Gui) (*commands.Commit, error) {
|
||||||
v, err := g.View("commits")
|
v, err := g.View("commits")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if len(gui.State.Commits) == 0 {
|
if len(gui.State.Commits) == 0 {
|
||||||
return commands.Commit{}, errors.New(gui.Tr.SLocalize("NoCommitsThisBranch"))
|
return &commands.Commit{}, errors.New(gui.Tr.SLocalize("NoCommitsThisBranch"))
|
||||||
}
|
}
|
||||||
lineNumber := gui.getItemPosition(v)
|
lineNumber := gui.getItemPosition(v)
|
||||||
if lineNumber > len(gui.State.Commits)-1 {
|
if lineNumber > len(gui.State.Commits)-1 {
|
||||||
|
@ -7,16 +7,17 @@ import (
|
|||||||
|
|
||||||
// "strings"
|
// "strings"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) stagedFiles() []commands.File {
|
func (gui *Gui) stagedFiles() []*commands.File {
|
||||||
files := gui.State.Files
|
files := gui.State.Files
|
||||||
result := make([]commands.File, 0)
|
result := make([]*commands.File, 0)
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if file.HasStagedChanges {
|
if file.HasStagedChanges {
|
||||||
result = append(result, file)
|
result = append(result, file)
|
||||||
@ -25,9 +26,9 @@ func (gui *Gui) stagedFiles() []commands.File {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) trackedFiles() []commands.File {
|
func (gui *Gui) trackedFiles() []*commands.File {
|
||||||
files := gui.State.Files
|
files := gui.State.Files
|
||||||
result := make([]commands.File, 0)
|
result := make([]*commands.File, 0)
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if file.Tracked {
|
if file.Tracked {
|
||||||
result = append(result, file)
|
result = append(result, file)
|
||||||
@ -116,9 +117,9 @@ func (gui *Gui) handleAddPatch(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.Errors.ErrSubProcess
|
return gui.Errors.ErrSubProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSelectedFile(g *gocui.Gui) (commands.File, error) {
|
func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
|
||||||
if len(gui.State.Files) == 0 {
|
if len(gui.State.Files) == 0 {
|
||||||
return commands.File{}, gui.Errors.ErrNoFiles
|
return &commands.File{}, gui.Errors.ErrNoFiles
|
||||||
}
|
}
|
||||||
filesView, err := g.View("files")
|
filesView, err := g.View("files")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -172,31 +173,7 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderfilesOptions(g *gocui.Gui, file *commands.File) error {
|
func (gui *Gui) renderfilesOptions(g *gocui.Gui, file *commands.File) error {
|
||||||
optionsMap := map[string]string{
|
return gui.renderGlobalOptions(g)
|
||||||
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
|
|
||||||
"S": gui.Tr.SLocalize("stashFiles"),
|
|
||||||
"c": gui.Tr.SLocalize("CommitChanges"),
|
|
||||||
"o": gui.Tr.SLocalize("open"),
|
|
||||||
"i": gui.Tr.SLocalize("ignore"),
|
|
||||||
"d": gui.Tr.SLocalize("delete"),
|
|
||||||
"space": gui.Tr.SLocalize("toggleStaged"),
|
|
||||||
"R": gui.Tr.SLocalize("refresh"),
|
|
||||||
"t": gui.Tr.SLocalize("addPatch"),
|
|
||||||
"e": gui.Tr.SLocalize("edit"),
|
|
||||||
"a": gui.Tr.SLocalize("toggleStagedAll"),
|
|
||||||
"PgUp/PgDn": gui.Tr.SLocalize("scroll"),
|
|
||||||
}
|
|
||||||
if gui.State.HasMergeConflicts {
|
|
||||||
optionsMap["a"] = gui.Tr.SLocalize("abortMerge")
|
|
||||||
optionsMap["m"] = gui.Tr.SLocalize("resolveMergeConflicts")
|
|
||||||
}
|
|
||||||
if file == nil {
|
|
||||||
return gui.renderOptionsMap(g, optionsMap)
|
|
||||||
}
|
|
||||||
if file.Tracked {
|
|
||||||
optionsMap["d"] = gui.Tr.SLocalize("checkout")
|
|
||||||
}
|
|
||||||
return gui.renderOptionsMap(g, optionsMap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -208,7 +185,9 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
|||||||
gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
|
gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
|
||||||
return gui.renderfilesOptions(g, nil)
|
return gui.renderfilesOptions(g, nil)
|
||||||
}
|
}
|
||||||
gui.renderfilesOptions(g, &file)
|
if err := gui.renderfilesOptions(g, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var content string
|
var content string
|
||||||
if file.HasMergeConflicts {
|
if file.HasMergeConflicts {
|
||||||
return gui.refreshMergePanel(g)
|
return gui.refreshMergePanel(g)
|
||||||
@ -299,24 +278,6 @@ func (gui *Gui) updateHasMergeConflictStatus() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderFile(file commands.File, filesView *gocui.View) {
|
|
||||||
// potentially inefficient to be instantiating these color
|
|
||||||
// objects with each render
|
|
||||||
red := color.New(color.FgRed)
|
|
||||||
green := color.New(color.FgGreen)
|
|
||||||
if !file.Tracked && !file.HasStagedChanges {
|
|
||||||
red.Fprintln(filesView, file.DisplayString)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
green.Fprint(filesView, file.DisplayString[0:1])
|
|
||||||
red.Fprint(filesView, file.DisplayString[1:3])
|
|
||||||
if file.HasUnstagedChanges {
|
|
||||||
red.Fprintln(filesView, file.Name)
|
|
||||||
} else {
|
|
||||||
green.Fprintln(filesView, file.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
||||||
item, err := gui.getSelectedFile(g)
|
item, err := gui.getSelectedFile(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -342,10 +303,14 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gui.refreshStateFiles()
|
gui.refreshStateFiles()
|
||||||
|
|
||||||
filesView.Clear()
|
filesView.Clear()
|
||||||
for _, file := range gui.State.Files {
|
list, err := utils.RenderList(gui.State.Files)
|
||||||
gui.renderFile(file, filesView)
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Fprint(filesView, list)
|
||||||
|
|
||||||
gui.correctCursor(filesView)
|
gui.correctCursor(filesView)
|
||||||
if filesView == g.CurrentView() {
|
if filesView == g.CurrentView() {
|
||||||
gui.handleFileSelect(g, filesView)
|
gui.handleFileSelect(g, filesView)
|
||||||
|
@ -73,10 +73,10 @@ type Gui struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type guiState struct {
|
type guiState struct {
|
||||||
Files []commands.File
|
Files []*commands.File
|
||||||
Branches []commands.Branch
|
Branches []*commands.Branch
|
||||||
Commits []commands.Commit
|
Commits []*commands.Commit
|
||||||
StashEntries []commands.StashEntry
|
StashEntries []*commands.StashEntry
|
||||||
PreviousView string
|
PreviousView string
|
||||||
HasMergeConflicts bool
|
HasMergeConflicts bool
|
||||||
ConflictIndex int
|
ConflictIndex int
|
||||||
@ -91,10 +91,10 @@ type guiState struct {
|
|||||||
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) {
|
func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) {
|
||||||
|
|
||||||
initialState := guiState{
|
initialState := guiState{
|
||||||
Files: make([]commands.File, 0),
|
Files: make([]*commands.File, 0),
|
||||||
PreviousView: "files",
|
PreviousView: "files",
|
||||||
Commits: make([]commands.Commit, 0),
|
Commits: make([]*commands.Commit, 0),
|
||||||
StashEntries: make([]commands.StashEntry, 0),
|
StashEntries: make([]*commands.StashEntry, 0),
|
||||||
ConflictIndex: 0,
|
ConflictIndex: 0,
|
||||||
ConflictTop: true,
|
ConflictTop: true,
|
||||||
Conflicts: make([]commands.Conflict, 0),
|
Conflicts: make([]commands.Conflict, 0),
|
||||||
@ -387,6 +387,15 @@ func (gui *Gui) renderAppStatus(g *gocui.Gui) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) renderGlobalOptions(g *gocui.Gui) error {
|
||||||
|
return gui.renderOptionsMap(g, map[string]string{
|
||||||
|
"PgUp/PgDn": gui.Tr.SLocalize("scroll"),
|
||||||
|
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
|
||||||
|
"esc/q": gui.Tr.SLocalize("close"),
|
||||||
|
"x": gui.Tr.SLocalize("menu"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) goEvery(g *gocui.Gui, interval time.Duration, function func(*gocui.Gui) error) {
|
func (gui *Gui) goEvery(g *gocui.Gui, interval time.Duration, function func(*gocui.Gui) error) {
|
||||||
go func() {
|
go func() {
|
||||||
for range time.Tick(interval) {
|
for range time.Tick(interval) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package gui
|
package gui
|
||||||
|
|
||||||
import "github.com/jesseduffield/gocui"
|
import (
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
)
|
||||||
|
|
||||||
// Binding - a keybinding mapping a key and modifier to a handler. The keypress
|
// Binding - a keybinding mapping a key and modifier to a handler. The keypress
|
||||||
// is only handled if the given view has focus, or handled globally if the view
|
// is only handled if the given view has focus, or handled globally if the view
|
||||||
@ -10,73 +12,377 @@ type Binding struct {
|
|||||||
Handler func(*gocui.Gui, *gocui.View) error
|
Handler func(*gocui.Gui, *gocui.View) error
|
||||||
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
|
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
|
||||||
Modifier gocui.Modifier
|
Modifier gocui.Modifier
|
||||||
|
KeyReadable string
|
||||||
|
Description string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) keybindings(g *gocui.Gui) error {
|
// GetDisplayStrings returns the display string of a file
|
||||||
bindings := []Binding{
|
func (b *Binding) GetDisplayStrings() []string {
|
||||||
{ViewName: "", Key: 'q', Modifier: gocui.ModNone, Handler: gui.quit},
|
return []string{b.GetKey(), b.Description}
|
||||||
{ViewName: "", Key: gocui.KeyCtrlC, Modifier: gocui.ModNone, Handler: gui.quit},
|
}
|
||||||
{ViewName: "", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: gui.quit},
|
|
||||||
{ViewName: "", Key: gocui.KeyPgup, Modifier: gocui.ModNone, Handler: gui.scrollUpMain},
|
func (b *Binding) GetKey() string {
|
||||||
{ViewName: "", Key: gocui.KeyPgdn, Modifier: gocui.ModNone, Handler: gui.scrollDownMain},
|
r, ok := b.Key.(rune)
|
||||||
{ViewName: "", Key: gocui.KeyCtrlU, Modifier: gocui.ModNone, Handler: gui.scrollUpMain},
|
key := ""
|
||||||
{ViewName: "", Key: gocui.KeyCtrlD, Modifier: gocui.ModNone, Handler: gui.scrollDownMain},
|
|
||||||
{ViewName: "", Key: 'P', Modifier: gocui.ModNone, Handler: gui.pushFiles},
|
if ok {
|
||||||
{ViewName: "", Key: 'p', Modifier: gocui.ModNone, Handler: gui.pullFiles},
|
key = string(r)
|
||||||
{ViewName: "", Key: 'R', Modifier: gocui.ModNone, Handler: gui.handleRefresh},
|
} else if b.KeyReadable != "" {
|
||||||
{ViewName: "status", Key: 'e', Modifier: gocui.ModNone, Handler: gui.handleEditConfig},
|
key = b.KeyReadable
|
||||||
{ViewName: "status", Key: 'o', Modifier: gocui.ModNone, Handler: gui.handleOpenConfig},
|
}
|
||||||
{ViewName: "status", Key: 'u', Modifier: gocui.ModNone, Handler: gui.handleCheckForUpdate},
|
|
||||||
{ViewName: "status", Key: 's', Modifier: gocui.ModNone, Handler: gui.handleSwitchRepo},
|
return key
|
||||||
{ViewName: "files", Key: 'c', Modifier: gocui.ModNone, Handler: gui.handleCommitPress},
|
}
|
||||||
{ViewName: "files", Key: 'C', Modifier: gocui.ModNone, Handler: gui.handleCommitEditorPress},
|
|
||||||
{ViewName: "files", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleFilePress},
|
func (gui *Gui) GetKeybindings() []*Binding {
|
||||||
{ViewName: "files", Key: 'd', Modifier: gocui.ModNone, Handler: gui.handleFileRemove},
|
bindings := []*Binding{
|
||||||
{ViewName: "files", Key: 'm', Modifier: gocui.ModNone, Handler: gui.handleSwitchToMerge},
|
{
|
||||||
{ViewName: "files", Key: 'e', Modifier: gocui.ModNone, Handler: gui.handleFileEdit},
|
ViewName: "",
|
||||||
{ViewName: "files", Key: 'o', Modifier: gocui.ModNone, Handler: gui.handleFileOpen},
|
Key: 'q',
|
||||||
{ViewName: "files", Key: 'i', Modifier: gocui.ModNone, Handler: gui.handleIgnoreFile},
|
Modifier: gocui.ModNone,
|
||||||
{ViewName: "files", Key: 'r', Modifier: gocui.ModNone, Handler: gui.handleRefreshFiles},
|
Handler: gui.quit,
|
||||||
{ViewName: "files", Key: 'S', Modifier: gocui.ModNone, Handler: gui.handleStashSave},
|
}, {
|
||||||
{ViewName: "files", Key: 'A', Modifier: gocui.ModNone, Handler: gui.handleAbortMerge},
|
ViewName: "",
|
||||||
{ViewName: "files", Key: 'a', Modifier: gocui.ModNone, Handler: gui.handleStageAll},
|
Key: gocui.KeyCtrlC,
|
||||||
{ViewName: "files", Key: 't', Modifier: gocui.ModNone, Handler: gui.handleAddPatch},
|
Modifier: gocui.ModNone,
|
||||||
{ViewName: "files", Key: 'D', Modifier: gocui.ModNone, Handler: gui.handleResetHard},
|
Handler: gui.quit,
|
||||||
{ViewName: "main", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: gui.handleEscapeMerge},
|
}, {
|
||||||
{ViewName: "main", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handlePickHunk},
|
ViewName: "",
|
||||||
{ViewName: "main", Key: 'b', Modifier: gocui.ModNone, Handler: gui.handlePickBothHunks},
|
Key: gocui.KeyEsc,
|
||||||
{ViewName: "main", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.handleSelectPrevConflict},
|
Modifier: gocui.ModNone,
|
||||||
{ViewName: "main", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.handleSelectNextConflict},
|
Handler: gui.quit,
|
||||||
{ViewName: "main", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleSelectTop},
|
}, {
|
||||||
{ViewName: "main", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleSelectBottom},
|
ViewName: "",
|
||||||
{ViewName: "main", Key: 'h', Modifier: gocui.ModNone, Handler: gui.handleSelectPrevConflict},
|
Key: gocui.KeyPgup,
|
||||||
{ViewName: "main", Key: 'l', Modifier: gocui.ModNone, Handler: gui.handleSelectNextConflict},
|
Modifier: gocui.ModNone,
|
||||||
{ViewName: "main", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleSelectTop},
|
Handler: gui.scrollUpMain,
|
||||||
{ViewName: "main", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleSelectBottom},
|
}, {
|
||||||
{ViewName: "main", Key: 'z', Modifier: gocui.ModNone, Handler: gui.handlePopFileSnapshot},
|
ViewName: "",
|
||||||
{ViewName: "branches", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleBranchPress},
|
Key: gocui.KeyPgdn,
|
||||||
{ViewName: "branches", Key: 'c', Modifier: gocui.ModNone, Handler: gui.handleCheckoutByName},
|
Modifier: gocui.ModNone,
|
||||||
{ViewName: "branches", Key: 'F', Modifier: gocui.ModNone, Handler: gui.handleForceCheckout},
|
Handler: gui.scrollDownMain,
|
||||||
{ViewName: "branches", Key: 'n', Modifier: gocui.ModNone, Handler: gui.handleNewBranch},
|
}, {
|
||||||
{ViewName: "branches", Key: 'd', Modifier: gocui.ModNone, Handler: gui.handleDeleteBranch},
|
ViewName: "",
|
||||||
{ViewName: "branches", Key: 'D', Modifier: gocui.ModNone, Handler: gui.handleForceDeleteBranch},
|
Key: gocui.KeyCtrlU,
|
||||||
{ViewName: "branches", Key: 'm', Modifier: gocui.ModNone, Handler: gui.handleMerge},
|
Modifier: gocui.ModNone,
|
||||||
{ViewName: "commits", Key: 's', Modifier: gocui.ModNone, Handler: gui.handleCommitSquashDown},
|
Handler: gui.scrollUpMain,
|
||||||
{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: gui.handleRenameCommit},
|
}, {
|
||||||
{ViewName: "commits", Key: 'R', Modifier: gocui.ModNone, Handler: gui.handleRenameCommitEditor},
|
ViewName: "",
|
||||||
{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: gui.handleResetToCommit},
|
Key: gocui.KeyCtrlD,
|
||||||
{ViewName: "commits", Key: 'f', Modifier: gocui.ModNone, Handler: gui.handleCommitFixup},
|
Modifier: gocui.ModNone,
|
||||||
{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleStashApply},
|
Handler: gui.scrollDownMain,
|
||||||
{ViewName: "stash", Key: 'g', Modifier: gocui.ModNone, Handler: gui.handleStashPop},
|
}, {
|
||||||
{ViewName: "stash", Key: 'd', Modifier: gocui.ModNone, Handler: gui.handleStashDrop},
|
ViewName: "",
|
||||||
{ViewName: "commitMessage", Key: gocui.KeyEnter, Modifier: gocui.ModNone, Handler: gui.handleCommitConfirm},
|
Key: 'P',
|
||||||
{ViewName: "commitMessage", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: gui.handleCommitClose},
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.pushFiles,
|
||||||
|
Description: gui.Tr.SLocalize("push"),
|
||||||
|
}, {
|
||||||
|
ViewName: "",
|
||||||
|
Key: 'p',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.pullFiles,
|
||||||
|
Description: gui.Tr.SLocalize("pull"),
|
||||||
|
}, {
|
||||||
|
ViewName: "",
|
||||||
|
Key: 'R',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleRefresh,
|
||||||
|
Description: gui.Tr.SLocalize("refresh"),
|
||||||
|
}, {
|
||||||
|
ViewName: "",
|
||||||
|
Key: 'x',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCreateOptionsMenu,
|
||||||
|
}, {
|
||||||
|
ViewName: "status",
|
||||||
|
Key: 'e',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleEditConfig,
|
||||||
|
Description: gui.Tr.SLocalize("EditConfig"),
|
||||||
|
}, {
|
||||||
|
ViewName: "status",
|
||||||
|
Key: 'o',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleOpenConfig,
|
||||||
|
Description: gui.Tr.SLocalize("OpenConfig"),
|
||||||
|
}, {
|
||||||
|
ViewName: "status",
|
||||||
|
Key: 'u',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCheckForUpdate,
|
||||||
|
Description: gui.Tr.SLocalize("checkForUpdate"),
|
||||||
|
}, {
|
||||||
|
ViewName: "status",
|
||||||
|
Key: 's',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSwitchRepo,
|
||||||
|
Description: gui.Tr.SLocalize("SwitchRepo"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'c',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCommitPress,
|
||||||
|
Description: gui.Tr.SLocalize("CommitChanges"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'C',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCommitEditorPress,
|
||||||
|
Description: gui.Tr.SLocalize("CommitChangesWithEditor"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: gocui.KeySpace,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleFilePress,
|
||||||
|
KeyReadable: "space",
|
||||||
|
Description: gui.Tr.SLocalize("toggleStaged"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'd',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleFileRemove,
|
||||||
|
Description: gui.Tr.SLocalize("removeFile"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'm',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSwitchToMerge,
|
||||||
|
Description: gui.Tr.SLocalize("resolveMergeConflicts"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'e',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleFileEdit,
|
||||||
|
Description: gui.Tr.SLocalize("editFile"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'o',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleFileOpen,
|
||||||
|
Description: gui.Tr.SLocalize("openFile"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'i',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleIgnoreFile,
|
||||||
|
Description: gui.Tr.SLocalize("ignoreFile"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'r',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleRefreshFiles,
|
||||||
|
Description: gui.Tr.SLocalize("refreshFiles"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'S',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleStashSave,
|
||||||
|
Description: gui.Tr.SLocalize("stashFiles"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'A',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleAbortMerge,
|
||||||
|
Description: gui.Tr.SLocalize("abortMerge"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'a',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleStageAll,
|
||||||
|
Description: gui.Tr.SLocalize("toggleStagedAll"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 't',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleAddPatch,
|
||||||
|
Description: gui.Tr.SLocalize("addPatch"),
|
||||||
|
}, {
|
||||||
|
ViewName: "files",
|
||||||
|
Key: 'D',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleResetHard,
|
||||||
|
Description: gui.Tr.SLocalize("resetHard"),
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: gocui.KeyEsc,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleEscapeMerge,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: gocui.KeySpace,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handlePickHunk,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: 'b',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handlePickBothHunks,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: gocui.KeyArrowLeft,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSelectPrevConflict,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: gocui.KeyArrowRight,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSelectNextConflict,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: gocui.KeyArrowUp,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSelectTop,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: gocui.KeyArrowDown,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSelectBottom,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: 'h',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSelectPrevConflict,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: 'l',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSelectNextConflict,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: 'k',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSelectTop,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: 'j',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleSelectBottom,
|
||||||
|
}, {
|
||||||
|
ViewName: "main",
|
||||||
|
Key: 'z',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handlePopFileSnapshot,
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: gocui.KeySpace,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleBranchPress,
|
||||||
|
KeyReadable: "space",
|
||||||
|
Description: gui.Tr.SLocalize("checkout"),
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: 'c',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCheckoutByName,
|
||||||
|
Description: gui.Tr.SLocalize("checkoutByName"),
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: 'F',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleForceCheckout,
|
||||||
|
Description: gui.Tr.SLocalize("forceCheckout"),
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: 'n',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleNewBranch,
|
||||||
|
Description: gui.Tr.SLocalize("newBranch"),
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: 'd',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleDeleteBranch,
|
||||||
|
Description: gui.Tr.SLocalize("deleteBranch"),
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: 'D',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleForceDeleteBranch,
|
||||||
|
Description: gui.Tr.SLocalize("forceDeleteBranch"),
|
||||||
|
}, {
|
||||||
|
ViewName: "branches",
|
||||||
|
Key: 'm',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleMerge,
|
||||||
|
Description: gui.Tr.SLocalize("mergeIntoCurrentBranch"),
|
||||||
|
}, {
|
||||||
|
ViewName: "commits",
|
||||||
|
Key: 's',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCommitSquashDown,
|
||||||
|
Description: gui.Tr.SLocalize("squashDown"),
|
||||||
|
}, {
|
||||||
|
ViewName: "commits",
|
||||||
|
Key: 'r',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleRenameCommit,
|
||||||
|
Description: gui.Tr.SLocalize("renameCommit"),
|
||||||
|
}, {
|
||||||
|
ViewName: "commits",
|
||||||
|
Key: 'R',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleRenameCommitEditor,
|
||||||
|
Description: gui.Tr.SLocalize("renameCommitEditor"),
|
||||||
|
}, {
|
||||||
|
ViewName: "commits",
|
||||||
|
Key: 'g',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleResetToCommit,
|
||||||
|
Description: gui.Tr.SLocalize("resetToThisCommit"),
|
||||||
|
}, {
|
||||||
|
ViewName: "commits",
|
||||||
|
Key: 'f',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCommitFixup,
|
||||||
|
Description: gui.Tr.SLocalize("fixupCommit"),
|
||||||
|
}, {
|
||||||
|
ViewName: "stash",
|
||||||
|
Key: gocui.KeySpace,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleStashApply,
|
||||||
|
KeyReadable: "space",
|
||||||
|
Description: gui.Tr.SLocalize("apply"),
|
||||||
|
}, {
|
||||||
|
ViewName: "stash",
|
||||||
|
Key: 'g',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleStashPop,
|
||||||
|
Description: gui.Tr.SLocalize("pop"),
|
||||||
|
}, {
|
||||||
|
ViewName: "stash",
|
||||||
|
Key: 'd',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleStashDrop,
|
||||||
|
Description: gui.Tr.SLocalize("drop"),
|
||||||
|
}, {
|
||||||
|
ViewName: "commitMessage",
|
||||||
|
Key: gocui.KeyEnter,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCommitConfirm,
|
||||||
|
}, {
|
||||||
|
ViewName: "commitMessage",
|
||||||
|
Key: gocui.KeyEsc,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleCommitClose,
|
||||||
|
}, {
|
||||||
|
ViewName: "menu",
|
||||||
|
Key: gocui.KeyEsc,
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleMenuClose,
|
||||||
|
}, {
|
||||||
|
ViewName: "menu",
|
||||||
|
Key: 'q',
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleMenuClose,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Would make these keybindings global but that interferes with editing
|
// Would make these keybindings global but that interferes with editing
|
||||||
// input in the confirmation panel
|
// input in the confirmation panel
|
||||||
for _, viewName := range []string{"status", "files", "branches", "commits", "stash"} {
|
for _, viewName := range []string{"status", "files", "branches", "commits", "stash", "menu"} {
|
||||||
bindings = append(bindings, []Binding{
|
bindings = append(bindings, []*Binding{
|
||||||
{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
|
{ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||||
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
|
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||||
{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},
|
{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||||
@ -89,6 +395,12 @@ func (gui *Gui) keybindings(g *gocui.Gui) error {
|
|||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) keybindings(g *gocui.Gui) error {
|
||||||
|
bindings := gui.GetKeybindings()
|
||||||
|
|
||||||
for _, binding := range bindings {
|
for _, binding := range bindings {
|
||||||
if err := g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
|
if err := g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
|
||||||
return err
|
return err
|
||||||
|
71
pkg/gui/menu_panel.go
Normal file
71
pkg/gui/menu_panel.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package gui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
// doing nothing for now
|
||||||
|
// but it is needed for switch in newLineFocused
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) renderMenuOptions(g *gocui.Gui) error {
|
||||||
|
optionsMap := map[string]string{
|
||||||
|
"esc/q": gui.Tr.SLocalize("close"),
|
||||||
|
"↑ ↓": gui.Tr.SLocalize("navigate"),
|
||||||
|
"space": gui.Tr.SLocalize("execute"),
|
||||||
|
}
|
||||||
|
return gui.renderOptionsMap(g, optionsMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
if err := g.DeleteKeybinding("menu", gocui.KeySpace, gocui.ModNone); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err := g.DeleteView("menu")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return gui.returnFocus(g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error {
|
||||||
|
list, err := utils.RenderList(items)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, list)
|
||||||
|
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
|
||||||
|
menuView.Title = strings.Title(gui.Tr.SLocalize("menu"))
|
||||||
|
menuView.FgColor = gocui.ColorWhite
|
||||||
|
menuView.Clear()
|
||||||
|
fmt.Fprint(menuView, list)
|
||||||
|
|
||||||
|
if err := gui.renderMenuOptions(gui.g); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
lineNumber := gui.getItemPosition(v)
|
||||||
|
return handlePress(lineNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gui.g.SetKeybinding("menu", gocui.KeySpace, gocui.ModNone, wrappedHandlePress); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.g.Update(func(g *gocui.Gui) error {
|
||||||
|
if _, err := g.SetViewOnTop("menu"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
currentView := gui.g.CurrentView()
|
||||||
|
return gui.switchFocus(gui.g, currentView, menuView)
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
51
pkg/gui/options_menu_panel.go
Normal file
51
pkg/gui/options_menu_panel.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package gui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (gui *Gui) getBindings(v *gocui.View) []*Binding {
|
||||||
|
var (
|
||||||
|
bindingsGlobal, bindingsPanel []*Binding
|
||||||
|
)
|
||||||
|
|
||||||
|
bindings := gui.GetKeybindings()
|
||||||
|
|
||||||
|
for _, binding := range bindings {
|
||||||
|
if binding.GetKey() != "" && binding.Description != "" {
|
||||||
|
switch binding.ViewName {
|
||||||
|
case "":
|
||||||
|
bindingsGlobal = append(bindingsGlobal, binding)
|
||||||
|
case v.Name():
|
||||||
|
bindingsPanel = append(bindingsPanel, binding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// append dummy element to have a separator between
|
||||||
|
// panel and global keybindings
|
||||||
|
bindingsPanel = append(bindingsPanel, &Binding{})
|
||||||
|
return append(bindingsPanel, bindingsGlobal...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
bindings := gui.getBindings(v)
|
||||||
|
|
||||||
|
handleOptionsMenuPress := func(index int) error {
|
||||||
|
if bindings[index].Key == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if index <= len(bindings) {
|
||||||
|
return errors.New("Index is greater than size of bindings")
|
||||||
|
}
|
||||||
|
err := gui.handleMenuClose(g, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return bindings[index].Handler(g, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.createMenu(bindings, handleOptionsMenuPress)
|
||||||
|
}
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
|
func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
|
||||||
@ -14,10 +15,14 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
gui.State.StashEntries = gui.GitCommand.GetStashEntries()
|
gui.State.StashEntries = gui.GitCommand.GetStashEntries()
|
||||||
|
|
||||||
v.Clear()
|
v.Clear()
|
||||||
for _, stashEntry := range gui.State.StashEntries {
|
list, err := utils.RenderList(gui.State.StashEntries)
|
||||||
fmt.Fprintln(v, stashEntry.DisplayString)
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
fmt.Fprint(v, list)
|
||||||
|
|
||||||
return gui.resetOrigin(v)
|
return gui.resetOrigin(v)
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
@ -27,17 +32,13 @@ func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry {
|
|||||||
if len(gui.State.StashEntries) == 0 {
|
if len(gui.State.StashEntries) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
lineNumber := gui.getItemPosition(v)
|
stashView, _ := gui.g.View("stash")
|
||||||
return &gui.State.StashEntries[lineNumber]
|
lineNumber := gui.getItemPosition(stashView)
|
||||||
|
return gui.State.StashEntries[lineNumber]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderStashOptions(g *gocui.Gui) error {
|
func (gui *Gui) renderStashOptions(g *gocui.Gui) error {
|
||||||
return gui.renderOptionsMap(g, map[string]string{
|
return gui.renderGlobalOptions(g)
|
||||||
"space": gui.Tr.SLocalize("apply"),
|
|
||||||
"g": gui.Tr.SLocalize("pop"),
|
|
||||||
"d": gui.Tr.SLocalize("drop"),
|
|
||||||
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
@ -42,11 +42,7 @@ func (gui *Gui) refreshStatus(g *gocui.Gui) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderStatusOptions(g *gocui.Gui) error {
|
func (gui *Gui) renderStatusOptions(g *gocui.Gui) error {
|
||||||
return gui.renderOptionsMap(g, map[string]string{
|
return gui.renderGlobalOptions(g)
|
||||||
"o": gui.Tr.SLocalize("OpenConfig"),
|
|
||||||
"e": gui.Tr.SLocalize("EditConfig"),
|
|
||||||
"u": gui.Tr.SLocalize("CheckForUpdate"),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
@ -82,6 +82,8 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error {
|
|||||||
mainView.SetOrigin(0, 0)
|
mainView.SetOrigin(0, 0)
|
||||||
|
|
||||||
switch v.Name() {
|
switch v.Name() {
|
||||||
|
case "menu":
|
||||||
|
return gui.handleMenuSelect(g, v)
|
||||||
case "status":
|
case "status":
|
||||||
return gui.handleStatusSelect(g, v)
|
return gui.handleStatusSelect(g, v)
|
||||||
case "files":
|
case "files":
|
||||||
@ -145,6 +147,7 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
g.Cursor = newView.Editable
|
g.Cursor = newView.Editable
|
||||||
|
|
||||||
return gui.newLineFocused(g, newView)
|
return gui.newLineFocused(g, newView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +160,6 @@ func (gui *Gui) getItemPosition(v *gocui.View) int {
|
|||||||
|
|
||||||
func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error {
|
||||||
// swallowing cursor movements in main
|
// swallowing cursor movements in main
|
||||||
// TODO: pull this out
|
|
||||||
if v == nil || v.Name() == "main" {
|
if v == nil || v.Name() == "main" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -176,19 +178,28 @@ func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error {
|
|||||||
|
|
||||||
func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error {
|
||||||
// swallowing cursor movements in main
|
// swallowing cursor movements in main
|
||||||
// TODO: pull this out
|
|
||||||
if v == nil || v.Name() == "main" {
|
if v == nil || v.Name() == "main" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cx, cy := v.Cursor()
|
cx, cy := v.Cursor()
|
||||||
ox, oy := v.Origin()
|
ox, oy := v.Origin()
|
||||||
if cy+oy >= len(v.BufferLines())-2 {
|
ly := len(v.BufferLines()) - 1
|
||||||
|
_, height := v.Size()
|
||||||
|
maxY := height - 1
|
||||||
|
|
||||||
|
// if we are at the end we just return
|
||||||
|
if cy+oy == ly {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := v.SetCursor(cx, cy+1); err != nil {
|
|
||||||
if err := v.SetOrigin(ox, oy+1); err != nil {
|
var err error
|
||||||
return err
|
if cy < maxY {
|
||||||
|
err = v.SetCursor(cx, cy+1)
|
||||||
|
} else {
|
||||||
|
err = v.SetOrigin(ox, oy+1)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.newLineFocused(g, v)
|
gui.newLineFocused(g, v)
|
||||||
@ -205,10 +216,19 @@ func (gui *Gui) resetOrigin(v *gocui.View) error {
|
|||||||
// if the cursor down past the last item, move it to the last line
|
// if the cursor down past the last item, move it to the last line
|
||||||
func (gui *Gui) correctCursor(v *gocui.View) error {
|
func (gui *Gui) correctCursor(v *gocui.View) error {
|
||||||
cx, cy := v.Cursor()
|
cx, cy := v.Cursor()
|
||||||
_, oy := v.Origin()
|
ox, oy := v.Origin()
|
||||||
lineCount := len(v.BufferLines()) - 2
|
_, height := v.Size()
|
||||||
if cy >= lineCount-oy {
|
maxY := height - 1
|
||||||
return v.SetCursor(cx, lineCount-oy)
|
ly := len(v.BufferLines()) - 1
|
||||||
|
if oy+cy <= ly {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newCy := utils.Min(ly, maxY)
|
||||||
|
if err := v.SetCursor(cx, newCy); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := v.SetOrigin(ox, ly-newCy); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -245,6 +265,7 @@ func (gui *Gui) renderOptionsMap(g *gocui.Gui, optionsMap map[string]string) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor properly
|
// TODO: refactor properly
|
||||||
|
// i'm so sorry but had to add this getBranchesView
|
||||||
func (gui *Gui) getFilesView(g *gocui.Gui) *gocui.View {
|
func (gui *Gui) getFilesView(g *gocui.Gui) *gocui.View {
|
||||||
v, _ := g.View("files")
|
v, _ := g.View("files")
|
||||||
return v
|
return v
|
||||||
@ -260,6 +281,11 @@ func (gui *Gui) getCommitMessageView(g *gocui.Gui) *gocui.View {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) getBranchesView(g *gocui.Gui) *gocui.View {
|
||||||
|
v, _ := g.View("branches")
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) trimmedContent(v *gocui.View) string {
|
func (gui *Gui) trimmedContent(v *gocui.View) string {
|
||||||
return strings.TrimSpace(v.Buffer())
|
return strings.TrimSpace(v.Buffer())
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,24 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CommitChanges",
|
ID: "CommitChanges",
|
||||||
Other: "Commit Veranderingen",
|
Other: "Commit Veranderingen",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "CommitChangesWithEditor",
|
||||||
|
Other: "commit Veranderingen met de git editor",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "StatusTitle",
|
ID: "StatusTitle",
|
||||||
Other: "Status",
|
Other: "Status",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "GlobalTitle",
|
||||||
|
Other: "Global",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "navigate",
|
ID: "navigate",
|
||||||
Other: "navigeer",
|
Other: "navigeer",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "menu",
|
||||||
|
Other: "menu",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "execute",
|
||||||
|
Other: "uitvoeren",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "stashFiles",
|
ID: "stashFiles",
|
||||||
Other: "stash-bestanden",
|
Other: "stash-bestanden",
|
||||||
@ -66,7 +78,7 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
Other: "verandering toevoegen",
|
Other: "verandering toevoegen",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "edit",
|
ID: "edit",
|
||||||
Other: "veranderen",
|
Other: "verander",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "scroll",
|
ID: "scroll",
|
||||||
Other: "scroll",
|
Other: "scroll",
|
||||||
@ -172,6 +184,9 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CloseConfirm",
|
ID: "CloseConfirm",
|
||||||
Other: "{{.keyBindClose}}: Sluiten, {{.keyBindConfirm}}: Bevestigen",
|
Other: "{{.keyBindClose}}: Sluiten, {{.keyBindConfirm}}: Bevestigen",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "close",
|
||||||
|
Other: "sluiten",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureResetThisCommit",
|
ID: "SureResetThisCommit",
|
||||||
Other: "Weet je het zeker dat je wil resetten naar deze commit?",
|
Other: "Weet je het zeker dat je wil resetten naar deze commit?",
|
||||||
@ -212,8 +227,11 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
ID: "OnlyRenameTopCommit",
|
ID: "OnlyRenameTopCommit",
|
||||||
Other: "Je kan alleen de bovenste commit hernoemen",
|
Other: "Je kan alleen de bovenste commit hernoemen",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "RenameCommit",
|
ID: "renameCommit",
|
||||||
Other: "Hernoem Commit",
|
Other: "hernoem commit",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "renameCommitEditor",
|
||||||
|
Other: "rename commit with editor",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "PotentialErrInGetselectedCommit",
|
ID: "PotentialErrInGetselectedCommit",
|
||||||
Other: "Er is mogelijk een error in getSelected Commit (geen match tussen ui en state)",
|
Other: "Er is mogelijk een error in getSelected Commit (geen match tussen ui en state)",
|
||||||
@ -295,9 +313,63 @@ func addDutch(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "MergeAborted",
|
ID: "MergeAborted",
|
||||||
Other: "Merge afgebroken",
|
Other: "Merge afgebroken",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "OpenConfig",
|
||||||
|
Other: "open config file",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "EditConfig",
|
||||||
|
Other: "verander config file",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ForcePush",
|
||||||
|
Other: "Forceer push",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ForcePushPrompt",
|
||||||
|
Other: "Jou branch is afgeweken van de remote branch. Druk 'esc' om te anuleren, of 'enter' om geforceert te pushen.",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "checkForUpdate",
|
||||||
|
Other: "check voor updates",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "CheckingForUpdates",
|
||||||
|
Other: "checken voor updates...",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "OnLatestVersionErr",
|
||||||
|
Other: "Je hebt al de laatste versie",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "MajorVersionErr",
|
||||||
|
Other: "Nieuwe versie ({{.newVersion}}) is niet teruggaand compatibele vergeleken met de huidige versie ({{.currentVersion}})",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "CouldNotFindBinaryErr",
|
||||||
|
Other: "Kon geen binary vinden op {{.url}}",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "AnonymousReportingTitle",
|
||||||
|
Other: "Help maak lazygit beter",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "AnonymousReportingPrompt",
|
||||||
|
Other: "Zou je anonieme data rapportage willen aanzetten om lazygit beter te kunnen maken? (enter/esc)",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "removeFile",
|
||||||
|
Other: `Verwijder als untracked / uitchecken wordt gevolgd (ga weg)`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "editFile",
|
||||||
|
Other: `verander bestand`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "openFile",
|
||||||
|
Other: `open bestand`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ignoreFile",
|
||||||
|
Other: `voeg toe aan .gitignore`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "refreshFiles",
|
||||||
|
Other: `refresh bestanden`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "resetHard",
|
||||||
|
Other: `harde reset`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "mergeIntoCurrentBranch",
|
||||||
|
Other: `merge in met huidige checked out branch`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "ConfirmQuit",
|
ID: "ConfirmQuit",
|
||||||
Other: `Are you sure you want to quit?`,
|
Other: `Weet je zeker dat je dit programma wil sluiten?`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,24 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CommitChanges",
|
ID: "CommitChanges",
|
||||||
Other: "commit changes",
|
Other: "commit changes",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "CommitChangesWithEditor",
|
||||||
|
Other: "commit changes using git editor",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "StatusTitle",
|
ID: "StatusTitle",
|
||||||
Other: "Status",
|
Other: "Status",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "GlobalTitle",
|
||||||
|
Other: "Global",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "navigate",
|
ID: "navigate",
|
||||||
Other: "navigate",
|
Other: "navigate",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "menu",
|
||||||
|
Other: "menu",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "execute",
|
||||||
|
Other: "execute",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "stashFiles",
|
ID: "stashFiles",
|
||||||
Other: "stash files",
|
Other: "stash files",
|
||||||
@ -69,6 +81,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "refresh",
|
ID: "refresh",
|
||||||
Other: "refresh",
|
Other: "refresh",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "push",
|
||||||
|
Other: "push",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "pull",
|
||||||
|
Other: "pull",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "addPatch",
|
ID: "addPatch",
|
||||||
Other: "add patch",
|
Other: "add patch",
|
||||||
@ -183,6 +201,9 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CloseConfirm",
|
ID: "CloseConfirm",
|
||||||
Other: "{{.keyBindClose}}: close, {{.keyBindConfirm}}: confirm",
|
Other: "{{.keyBindClose}}: close, {{.keyBindConfirm}}: confirm",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "close",
|
||||||
|
Other: "close",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureResetThisCommit",
|
ID: "SureResetThisCommit",
|
||||||
Other: "Are you sure you want to reset to this commit?",
|
Other: "Are you sure you want to reset to this commit?",
|
||||||
@ -223,8 +244,11 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
ID: "OnlyRenameTopCommit",
|
ID: "OnlyRenameTopCommit",
|
||||||
Other: "Can only rename topmost commit",
|
Other: "Can only rename topmost commit",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "RenameCommit",
|
ID: "renameCommit",
|
||||||
Other: "Rename Commit",
|
Other: "rename commit",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "renameCommitEditor",
|
||||||
|
Other: "rename commit with editor",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "PotentialErrInGetselectedCommit",
|
ID: "PotentialErrInGetselectedCommit",
|
||||||
Other: "potential error in getSelected Commit (mismatched ui and state)",
|
Other: "potential error in getSelected Commit (mismatched ui and state)",
|
||||||
@ -319,8 +343,8 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
ID: "ForcePushPrompt",
|
ID: "ForcePushPrompt",
|
||||||
Other: "Your branch has diverged from the remote branch. Press 'esc' to cancel, or 'enter' to force push.",
|
Other: "Your branch has diverged from the remote branch. Press 'esc' to cancel, or 'enter' to force push.",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CheckForUpdate",
|
ID: "checkForUpdate",
|
||||||
Other: "Check for update",
|
Other: "check for update",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CheckingForUpdates",
|
ID: "CheckingForUpdates",
|
||||||
Other: "Checking for updates...",
|
Other: "Checking for updates...",
|
||||||
@ -342,9 +366,33 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "GitconfigParseErr",
|
ID: "GitconfigParseErr",
|
||||||
Other: `Gogit failed to parse your gitconfig file due to the presence of unquoted '\' characters. Removing these should fix the issue.`,
|
Other: `Gogit failed to parse your gitconfig file due to the presence of unquoted '\' characters. Removing these should fix the issue.`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "removeFile",
|
||||||
|
Other: `delete if untracked / checkout if tracked`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "editFile",
|
||||||
|
Other: `edit file`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "openFile",
|
||||||
|
Other: `open file`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ignoreFile",
|
||||||
|
Other: `add to .gitignore`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "refreshFiles",
|
||||||
|
Other: `refresh files`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "resetHard",
|
||||||
|
Other: `reset hard`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "mergeIntoCurrentBranch",
|
||||||
|
Other: `merge into currently checked out branch`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "ConfirmQuit",
|
ID: "ConfirmQuit",
|
||||||
Other: `Are you sure you want to quit?`,
|
Other: `Are you sure you want to quit?`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "SwitchRepo",
|
||||||
|
Other: `Switch to a recent repo`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -32,12 +32,24 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CommitChanges",
|
ID: "CommitChanges",
|
||||||
Other: "commituj zmiany",
|
Other: "commituj zmiany",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "CommitChangesWithEditor",
|
||||||
|
Other: "commituj zmiany używając edytora z gita",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "StatusTitle",
|
ID: "StatusTitle",
|
||||||
Other: "Status",
|
Other: "Status",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "GlobalTitle",
|
||||||
|
Other: "Globalne",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "navigate",
|
ID: "navigate",
|
||||||
Other: "nawiguj",
|
Other: "nawiguj",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "menu",
|
||||||
|
Other: "menu",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "execute",
|
||||||
|
Other: "wykonaj",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "stashFiles",
|
ID: "stashFiles",
|
||||||
Other: "przechowaj pliki",
|
Other: "przechowaj pliki",
|
||||||
@ -134,6 +146,9 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "DeleteBranchMessage",
|
ID: "DeleteBranchMessage",
|
||||||
Other: "Jesteś pewien, że chcesz usunąć gałąź {{.selectedBranchName}} ?",
|
Other: "Jesteś pewien, że chcesz usunąć gałąź {{.selectedBranchName}} ?",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ForceDeleteBranchMessage",
|
||||||
|
Other: "Na pewno wymusić usunięcie gałęzi {{.selectedBranchName}}?",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CantMergeBranchIntoItself",
|
ID: "CantMergeBranchIntoItself",
|
||||||
Other: "Nie możesz scalić gałęzi do samej siebie",
|
Other: "Nie możesz scalić gałęzi do samej siebie",
|
||||||
@ -152,6 +167,9 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "deleteBranch",
|
ID: "deleteBranch",
|
||||||
Other: "usuń gałąź",
|
Other: "usuń gałąź",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "forceDeleteBranch",
|
||||||
|
Other: "usuń gałąź (wymuś)",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "NoBranchesThisRepo",
|
ID: "NoBranchesThisRepo",
|
||||||
Other: "Brak gałęzi dla tego repozytorium",
|
Other: "Brak gałęzi dla tego repozytorium",
|
||||||
@ -164,6 +182,9 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "CloseConfirm",
|
ID: "CloseConfirm",
|
||||||
Other: "{{.keyBindClose}}: zamknij, {{.keyBindConfirm}}: potwierdź",
|
Other: "{{.keyBindClose}}: zamknij, {{.keyBindConfirm}}: potwierdź",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "close",
|
||||||
|
Other: "zamknij",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "SureResetThisCommit",
|
ID: "SureResetThisCommit",
|
||||||
Other: "Jesteś pewny, że chcesz zresetować ten commit?",
|
Other: "Jesteś pewny, że chcesz zresetować ten commit?",
|
||||||
@ -204,8 +225,11 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
ID: "OnlyRenameTopCommit",
|
ID: "OnlyRenameTopCommit",
|
||||||
Other: "Można przmianować tylko najwyższy commit",
|
Other: "Można przmianować tylko najwyższy commit",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "RenameCommit",
|
ID: "renameCommit",
|
||||||
Other: "Przemianuj commit",
|
Other: "przemianuj commit",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "renameCommitEditor",
|
||||||
|
Other: "przemianuj commit w edytorze",
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "PotentialErrInGetselectedCommit",
|
ID: "PotentialErrInGetselectedCommit",
|
||||||
Other: "potencjalny błąd w getSelected Commit (niedopasowane ui i stan)",
|
Other: "potencjalny błąd w getSelected Commit (niedopasowane ui i stan)",
|
||||||
@ -287,6 +311,60 @@ func addPolish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "MergeAborted",
|
ID: "MergeAborted",
|
||||||
Other: "Scalanie anulowane",
|
Other: "Scalanie anulowane",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "OpenConfig",
|
||||||
|
Other: "otwórz plik konfiguracyjny",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "EditConfig",
|
||||||
|
Other: "edytuj plik konfiguracyjny",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ForcePush",
|
||||||
|
Other: "Wymuś wypchnięcie",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ForcePushPrompt",
|
||||||
|
Other: "Twoja gałąź rozeszła się z gałęzią zdalną. Wciśnij 'esc' aby anulować lub 'enter' aby wymusić wypchnięcie.",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "checkForUpdate",
|
||||||
|
Other: "sprawdź aktualizacje",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "CheckingForUpdates",
|
||||||
|
Other: "Sprawdzanie aktualizacji...",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "OnLatestVersionErr",
|
||||||
|
Other: "Już posiadasz najnowszą wersję",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "MajorVersionErr",
|
||||||
|
Other: "Nowa wersja ({{.newVersion}}) posiada niekompatybilne zmiany w porównaniu do obecnej wersji ({{.currentVersion}})",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "CouldNotFindBinaryErr",
|
||||||
|
Other: "Nie można znaleźć pliku binarnego w {{.url}}",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "AnonymousReportingTitle",
|
||||||
|
Other: "Help make lazygit better",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "AnonymousReportingPrompt",
|
||||||
|
Other: "Włączyć anonimowe raportowanie błędów w celu pomocy w usprawnianiu lazygita (enter/esc)?",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "removeFile",
|
||||||
|
Other: `usuń jeśli nie śledzony / przełącz jeśli śledzony`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "editFile",
|
||||||
|
Other: `edytuj plik`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "openFile",
|
||||||
|
Other: `otwórz plik`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "ignoreFile",
|
||||||
|
Other: `dodaj do .gitignore`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "refreshFiles",
|
||||||
|
Other: `odśwież pliki`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "resetHard",
|
||||||
|
Other: `zresetuj twardo`,
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "mergeIntoCurrentBranch",
|
||||||
|
Other: `scal do obecnej gałęzi`,
|
||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "ConfirmQuit",
|
ID: "ConfirmQuit",
|
||||||
Other: `Na pewno chcesz wyjść z programu?`,
|
Other: `Na pewno chcesz wyjść z programu?`,
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -99,3 +101,106 @@ func ResolvePlaceholderString(str string, arguments map[string]string) string {
|
|||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Min returns the minimum of two integers
|
||||||
|
func Min(x, y int) int {
|
||||||
|
if x < y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
type Displayable interface {
|
||||||
|
GetDisplayStrings() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderList takes a slice of items, confirms they implement the Displayable
|
||||||
|
// interface, then generates a list of their displaystrings to write to a panel's
|
||||||
|
// buffer
|
||||||
|
func RenderList(slice interface{}) (string, error) {
|
||||||
|
s := reflect.ValueOf(slice)
|
||||||
|
if s.Kind() != reflect.Slice {
|
||||||
|
return "", errors.New("RenderList given a non-slice type")
|
||||||
|
}
|
||||||
|
|
||||||
|
displayables := make([]Displayable, s.Len())
|
||||||
|
|
||||||
|
for i := 0; i < s.Len(); i++ {
|
||||||
|
value, ok := s.Index(i).Interface().(Displayable)
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("item does not implement the Displayable interface")
|
||||||
|
}
|
||||||
|
displayables[i] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderDisplayableList(displayables)
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderDisplayableList takes a list of displayable items, obtains their display
|
||||||
|
// strings via GetDisplayStrings() and then returns a single string containing
|
||||||
|
// each item's string representation on its own line, with appropriate horizontal
|
||||||
|
// padding between the item's own strings
|
||||||
|
func renderDisplayableList(items []Displayable) (string, error) {
|
||||||
|
if len(items) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
stringArrays := getDisplayStringArrays(items)
|
||||||
|
|
||||||
|
if !displayArraysAligned(stringArrays) {
|
||||||
|
return "", errors.New("Each item must return the same number of strings to display")
|
||||||
|
}
|
||||||
|
|
||||||
|
padWidths := getPadWidths(stringArrays)
|
||||||
|
paddedDisplayStrings := getPaddedDisplayStrings(stringArrays, padWidths)
|
||||||
|
|
||||||
|
return strings.Join(paddedDisplayStrings, "\n"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPadWidths(stringArrays [][]string) []int {
|
||||||
|
if len(stringArrays[0]) <= 1 {
|
||||||
|
return []int{}
|
||||||
|
}
|
||||||
|
padWidths := make([]int, len(stringArrays[0])-1)
|
||||||
|
for i := range padWidths {
|
||||||
|
for _, strings := range stringArrays {
|
||||||
|
if len(strings[i]) > padWidths[i] {
|
||||||
|
padWidths[i] = len(strings[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return padWidths
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string {
|
||||||
|
paddedDisplayStrings := make([]string, len(stringArrays))
|
||||||
|
for i, stringArray := range stringArrays {
|
||||||
|
if len(stringArray) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for j, padWidth := range padWidths {
|
||||||
|
paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " "
|
||||||
|
}
|
||||||
|
paddedDisplayStrings[i] += stringArray[len(padWidths)]
|
||||||
|
}
|
||||||
|
return paddedDisplayStrings
|
||||||
|
}
|
||||||
|
|
||||||
|
// displayArraysAligned returns true if every string array returned from our
|
||||||
|
// list of displayables has the same length
|
||||||
|
func displayArraysAligned(stringArrays [][]string) bool {
|
||||||
|
for _, strings := range stringArrays {
|
||||||
|
if len(strings) != len(stringArrays[0]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDisplayStringArrays(displayables []Displayable) [][]string {
|
||||||
|
stringArrays := make([][]string, len(displayables))
|
||||||
|
for i, item := range displayables {
|
||||||
|
stringArrays[i] = item.GetDisplayStrings()
|
||||||
|
}
|
||||||
|
return stringArrays
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -167,3 +168,181 @@ func TestResolvePlaceholderString(t *testing.T) {
|
|||||||
assert.EqualValues(t, string(s.expected), ResolvePlaceholderString(s.templateString, s.arguments))
|
assert.EqualValues(t, string(s.expected), ResolvePlaceholderString(s.templateString, s.arguments))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDisplayArraysAligned(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
input [][]string
|
||||||
|
expected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
[][]string{{"", ""}, {"", ""}},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[][]string{{""}, {"", ""}},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
assert.EqualValues(t, s.expected, displayArraysAligned(s.input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type myDisplayable struct {
|
||||||
|
strings []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type myStruct struct{}
|
||||||
|
|
||||||
|
func (d *myDisplayable) GetDisplayStrings() []string {
|
||||||
|
return d.strings
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDisplayStringArrays(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
input []Displayable
|
||||||
|
expected [][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
[]Displayable{
|
||||||
|
Displayable(&myDisplayable{[]string{"a", "b"}}),
|
||||||
|
Displayable(&myDisplayable{[]string{"c", "d"}}),
|
||||||
|
},
|
||||||
|
[][]string{{"a", "b"}, {"c", "d"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
assert.EqualValues(t, s.expected, getDisplayStringArrays(s.input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderDisplayableList(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
input []Displayable
|
||||||
|
expectedString string
|
||||||
|
expectedError error
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
[]Displayable{
|
||||||
|
Displayable(&myDisplayable{[]string{}}),
|
||||||
|
Displayable(&myDisplayable{[]string{}}),
|
||||||
|
},
|
||||||
|
"\n",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]Displayable{
|
||||||
|
Displayable(&myDisplayable{[]string{"aa", "b"}}),
|
||||||
|
Displayable(&myDisplayable{[]string{"c", "d"}}),
|
||||||
|
},
|
||||||
|
"aa b\nc d",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]Displayable{
|
||||||
|
Displayable(&myDisplayable{[]string{"a"}}),
|
||||||
|
Displayable(&myDisplayable{[]string{"b", "c"}}),
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
errors.New("Each item must return the same number of strings to display"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
str, err := renderDisplayableList(s.input)
|
||||||
|
assert.EqualValues(t, s.expectedString, str)
|
||||||
|
assert.EqualValues(t, s.expectedError, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderList(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
input interface{}
|
||||||
|
expectedString string
|
||||||
|
expectedError error
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
[]*myDisplayable{
|
||||||
|
{[]string{"aa", "b"}},
|
||||||
|
{[]string{"c", "d"}},
|
||||||
|
},
|
||||||
|
"aa b\nc d",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]*myStruct{
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
errors.New("item does not implement the Displayable interface"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&myStruct{},
|
||||||
|
"",
|
||||||
|
errors.New("RenderList given a non-slice type"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
str, err := RenderList(s.input)
|
||||||
|
assert.EqualValues(t, s.expectedString, str)
|
||||||
|
assert.EqualValues(t, s.expectedError, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPaddedDisplayStrings(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
stringArrays [][]string
|
||||||
|
padWidths []int
|
||||||
|
expected []string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
[][]string{{"a", "b"}, {"c", "d"}},
|
||||||
|
[]int{1},
|
||||||
|
[]string{"a b", "c d"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
assert.EqualValues(t, s.expected, getPaddedDisplayStrings(s.stringArrays, s.padWidths))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPadWidths(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
stringArrays [][]string
|
||||||
|
expected []int
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
[][]string{{""}, {""}},
|
||||||
|
[]int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[][]string{{"a"}, {""}},
|
||||||
|
[]int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[][]string{{"aa", "b", "ccc"}, {"c", "d", "e"}},
|
||||||
|
[]int{2, 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
54
scripts/generate_cheatsheet.go
Normal file
54
scripts/generate_cheatsheet.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// run:
|
||||||
|
// LANG=en go run generate_cheatsheet.go
|
||||||
|
// to generate Keybindings_en.md file in current directory
|
||||||
|
// change LANG to generate cheatsheet in different language (if supported)
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/app"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
appConfig, _ := config.NewAppConfig("", "", "", "", "", new(bool))
|
||||||
|
a, _ := app.NewApp(appConfig)
|
||||||
|
lang := a.Tr.GetLanguage()
|
||||||
|
name := "Keybindings_" + lang + ".md"
|
||||||
|
bindings := a.Gui.GetKeybindings()
|
||||||
|
padWidth := a.Gui.GetMaxKeyLength(bindings)
|
||||||
|
file, _ := os.Create(name)
|
||||||
|
current := "v"
|
||||||
|
content := ""
|
||||||
|
title := ""
|
||||||
|
|
||||||
|
file.WriteString("# Lazygit " + a.Tr.SLocalize("menu"))
|
||||||
|
|
||||||
|
for _, binding := range bindings {
|
||||||
|
if key := a.Gui.GetKey(binding); key != "" && (binding.Description != "" || key == "x") {
|
||||||
|
if binding.ViewName != current {
|
||||||
|
current = binding.ViewName
|
||||||
|
if current == "" {
|
||||||
|
title = a.Tr.SLocalize("GlobalTitle")
|
||||||
|
} else {
|
||||||
|
title = a.Tr.SLocalize(strings.Title(current) + "Title")
|
||||||
|
}
|
||||||
|
content = fmt.Sprintf("</pre>\n\n## %s\n<pre>\n", title)
|
||||||
|
file.WriteString(content)
|
||||||
|
}
|
||||||
|
// workaround to include menu keybinding in cheatsheet
|
||||||
|
// could not add this Description field directly to keybindings.go,
|
||||||
|
// because then menu key would be displayed in menu itself and that is undesirable
|
||||||
|
if key == "x" {
|
||||||
|
binding.Description = a.Tr.SLocalize("menu")
|
||||||
|
}
|
||||||
|
content = fmt.Sprintf("\t<kbd>%s</kbd>%s %s\n", key, strings.TrimPrefix(utils.WithPadding(key, padWidth), key), binding.Description)
|
||||||
|
file.WriteString(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user