mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-13 00:56:25 -04:00
Merge branch 'master' into gh-integration
This commit is contained in:
commit
4fead59416
13
go.mod
13
go.mod
|
|
@ -9,20 +9,18 @@ require (
|
|||
github.com/cli/safeexec v1.0.0
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fatih/color v1.9.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/go-errors/errors v1.4.1
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/iriri/minimal/gitignore v0.3.2 // indirect
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
|
|
@ -30,12 +28,11 @@ require (
|
|||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/kyokomi/emoji/v2 v2.2.8
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
github.com/mattn/go-colorable v0.1.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
github.com/onsi/gomega v1.7.1 // indirect
|
||||
github.com/ozeidan/fuzzy-patricia v3.0.0+incompatible // indirect
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
|
|
@ -43,7 +40,7 @@ require (
|
|||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
|
||||
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
|
||||
golang.org/x/sys v0.0.0-20211031064116-611d5d643895 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
|
||||
|
|
|
|||
45
go.sum
45
go.sum
|
|
@ -56,8 +56,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
|
|
@ -67,26 +67,12 @@ github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
|||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
|
||||
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
|
||||
github.com/iriri/minimal v0.0.0-20180828191352-9b2348d09c1a h1:mCZYG6QcX0dz/J0rFc1tcRYGeixlDcCGSPXuPMbiS5U=
|
||||
github.com/iriri/minimal/gitignore v0.3.2 h1:MnTVH89iuwiyZ/a1pByw/mAU2ShWai1yvv0tgHSq5Ww=
|
||||
github.com/iriri/minimal/gitignore v0.3.2/go.mod h1:v7YhsYBAInyAnQligwCIGRuQmtwQyYxkVy5vEdy2wPU=
|
||||
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-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017035223-b68948e63cc3 h1:J5s/4Y860tas8J0AMQ3gJKCbJPx8zNpiTm5UjEgPQfY=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017035223-b68948e63cc3/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017041119-0ec562dfd23b h1:kepukaDQfZ6LBSvHUYReFvVSW5Lx5ZQZDgGhXj0Mx7U=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017041119-0ec562dfd23b/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017063715-c74848d8ad00 h1:5TusU8ir9OHg3By2PPmLwa2y+2G9F+16QRK8bpofsC0=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017063715-c74848d8ad00/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017091015-8bf4a4666b77 h1:MQUxSxVBTZQpSYybEiFA4+oIi02ycTKGCqgHItYi/20=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017091015-8bf4a4666b77/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017220056-b2fc03c74a6f h1:JHrb78pj+gYC3KiJKL1WW6lYzlatBIF46oREn68plTM=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211017220056-b2fc03c74a6f/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0 h1:To4mMbu6oQpbbyHa4WtMTc/DHa9dqiRWZpDLMNK+Hdk=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/minimal v0.0.0-20211018110810-9cde264e6b1e h1:WZc73tBVMMhcO6zXyZBItLEF4jgBpBH0lFCZzDgrjDg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75 h1:zu+WBGwscCwu7GEuxANGl8E51HbW6ueqTF1XdAoqnZs=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
||||
|
|
@ -115,13 +101,13 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
|
|||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
|
|
@ -136,9 +122,6 @@ github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
|
|||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/ozeidan/fuzzy-patricia v1.0.1 h1:YExnavqXH3OvCCqE2TunuJJHdFcFQdVEfUoWzrnPxSg=
|
||||
github.com/ozeidan/fuzzy-patricia v3.0.0+incompatible h1:Pl61eMyfJqgY/wytiI4vamqPYribq6d8VxeP1CNyg9M=
|
||||
github.com/ozeidan/fuzzy-patricia v3.0.0+incompatible/go.mod h1:zgvuCcYS7wB7fVCGblsaFFmEe8+aAH13dTYm8FbrpsM=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
|
@ -188,17 +171,15 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 h1:KzbpndAYEM+4oHRp9JmB2ewj0NHHxO3Z0g7Gus2O1kk=
|
||||
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA=
|
||||
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211031064116-611d5d643895 h1:iaNpwpnrgL5jzWS0vCNnfa8HqzxveCFpFx3uC/X4Tps=
|
||||
golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
|
@ -207,6 +188,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func (gui *Gui) handleCopyCommit() error {
|
|||
return err
|
||||
}
|
||||
|
||||
item, ok := context.SelectedItem()
|
||||
item, ok := context.GetSelectedItem()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -652,7 +652,7 @@ func (gui *Gui) handleGotoBottomForCommitsPanel() error {
|
|||
}
|
||||
|
||||
for _, context := range gui.getListContexts() {
|
||||
if context.ViewName == "commits" {
|
||||
if context.GetViewName() == "commits" {
|
||||
return context.handleGotoBottom()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -285,9 +285,9 @@ func (gui *Gui) currentContextWithoutLock() Context {
|
|||
|
||||
// the status panel is not yet a list context (and may never be), so this method is not
|
||||
// quite the same as currentSideContext()
|
||||
func (gui *Gui) currentSideListContext() *ListContext {
|
||||
func (gui *Gui) currentSideListContext() IListContext {
|
||||
context := gui.currentSideContext()
|
||||
listContext, ok := context.(*ListContext)
|
||||
listContext, ok := context.(IListContext)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,19 +56,19 @@ var allContextKeys = []ContextKey{
|
|||
|
||||
type ContextTree struct {
|
||||
Status Context
|
||||
Files *ListContext
|
||||
Submodules *ListContext
|
||||
Menu *ListContext
|
||||
Branches *ListContext
|
||||
Remotes *ListContext
|
||||
RemoteBranches *ListContext
|
||||
Tags *ListContext
|
||||
BranchCommits *ListContext
|
||||
CommitFiles *ListContext
|
||||
ReflogCommits *ListContext
|
||||
SubCommits *ListContext
|
||||
Stash *ListContext
|
||||
Suggestions *ListContext
|
||||
Files IListContext
|
||||
Submodules IListContext
|
||||
Menu IListContext
|
||||
Branches IListContext
|
||||
Remotes IListContext
|
||||
RemoteBranches IListContext
|
||||
Tags IListContext
|
||||
BranchCommits IListContext
|
||||
CommitFiles IListContext
|
||||
ReflogCommits IListContext
|
||||
SubCommits IListContext
|
||||
Stash IListContext
|
||||
Suggestions IListContext
|
||||
Normal Context
|
||||
Staging Context
|
||||
PatchBuilding Context
|
||||
|
|
|
|||
|
|
@ -395,9 +395,7 @@ func (gui *Gui) handleCommitPress() error {
|
|||
prefix := rgx.ReplaceAllString(gui.getCheckedOutBranch().Name, prefixReplace)
|
||||
gui.Views.CommitMessage.ClearTextArea()
|
||||
gui.Views.CommitMessage.TextArea.TypeString(prefix)
|
||||
gui.g.Update(func(*gocui.Gui) error {
|
||||
return nil
|
||||
})
|
||||
gui.render()
|
||||
}
|
||||
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ import (
|
|||
"github.com/xo/terminfo"
|
||||
)
|
||||
|
||||
func TestRender(t *testing.T) {
|
||||
func init() {
|
||||
color.ForceSetColorLevel(terminfo.ColorLevelNone)
|
||||
}
|
||||
|
||||
func TestRender(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
root *FileNode
|
||||
|
|
|
|||
|
|
@ -464,6 +464,12 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *oscom
|
|||
return gui, nil
|
||||
}
|
||||
|
||||
var RuneReplacements = map[rune]string{
|
||||
// for the commit graph
|
||||
'⏣': "M",
|
||||
'⎔': "o",
|
||||
}
|
||||
|
||||
// Run setup the gui with keybindings and start the mainloop
|
||||
func (gui *Gui) Run() error {
|
||||
recordEvents := recordingEvents()
|
||||
|
|
@ -474,7 +480,7 @@ func (gui *Gui) Run() error {
|
|||
playMode = gocui.REPLAYING
|
||||
}
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, playMode, headless())
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, playMode, headless(), RuneReplacements)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -718,6 +724,7 @@ func (gui *Gui) startBackgroundFetch() {
|
|||
} else {
|
||||
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error {
|
||||
err := gui.fetch(false, "")
|
||||
gui.render()
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,24 +52,19 @@ func (gui *Gui) createAllViews() error {
|
|||
|
||||
gui.Views.Stash.Title = gui.Tr.StashTitle
|
||||
gui.Views.Stash.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Stash.ContainsList = true
|
||||
|
||||
gui.Views.Commits.Title = gui.Tr.CommitsTitle
|
||||
gui.Views.Commits.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Commits.ContainsList = true
|
||||
|
||||
gui.Views.CommitFiles.Title = gui.Tr.CommitFiles
|
||||
gui.Views.CommitFiles.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.CommitFiles.ContainsList = true
|
||||
|
||||
gui.Views.Branches.Title = gui.Tr.BranchesTitle
|
||||
gui.Views.Branches.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Branches.ContainsList = true
|
||||
|
||||
gui.Views.Files.Highlight = true
|
||||
gui.Views.Files.Title = gui.Tr.FilesTitle
|
||||
gui.Views.Files.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Files.ContainsList = true
|
||||
|
||||
gui.Views.Secondary.Title = gui.Tr.DiffTitle
|
||||
gui.Views.Secondary.Wrap = true
|
||||
|
|
@ -111,7 +106,6 @@ func (gui *Gui) createAllViews() error {
|
|||
gui.Views.Credentials.Editable = true
|
||||
|
||||
gui.Views.Suggestions.Visible = false
|
||||
gui.Views.Suggestions.ContainsList = true
|
||||
|
||||
gui.Views.Menu.Visible = false
|
||||
|
||||
|
|
@ -264,7 +258,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||
}
|
||||
|
||||
for _, listContext := range gui.getListContexts() {
|
||||
view, err := gui.g.View(listContext.ViewName)
|
||||
view, err := gui.g.View(listContext.GetViewName())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
|
@ -274,8 +268,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||
continue
|
||||
}
|
||||
|
||||
// check if the selected line is now out of view and if so refocus it
|
||||
view.FocusPoint(0, listContext.GetPanelState().GetSelectedLineIdx())
|
||||
listContext.FocusLine()
|
||||
|
||||
view.SelBgColor = theme.GocuiSelectedLineBgColor
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,49 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ListContext struct {
|
||||
GetItemsLength func() int
|
||||
GetDisplayStrings func() [][]string
|
||||
GetDisplayStrings func(startIdx int, length int) [][]string
|
||||
OnFocus func() error
|
||||
OnFocusLost func() error
|
||||
OnClickSelectedItem func() error
|
||||
|
||||
// the boolean here tells us whether the item is nil. This is needed because you can't work it out on the calling end once the pointer is wrapped in an interface (unless you want to use reflection)
|
||||
SelectedItem func() (ListItem, bool)
|
||||
GetPanelState func() IListPanelState
|
||||
SelectedItem func() (ListItem, bool)
|
||||
OnGetPanelState func() IListPanelState
|
||||
|
||||
Gui *Gui
|
||||
|
||||
*BasicContext
|
||||
}
|
||||
|
||||
type IListContext interface {
|
||||
GetSelectedItem() (ListItem, bool)
|
||||
GetSelectedItemId() string
|
||||
OnRender() error
|
||||
handlePrevLine() error
|
||||
handleNextLine() error
|
||||
handleLineChange(change int) error
|
||||
handleNextPage() error
|
||||
handleGotoTop() error
|
||||
handleGotoBottom() error
|
||||
handlePrevPage() error
|
||||
handleClick() error
|
||||
onSearchSelect(selectedLineIdx int) error
|
||||
FocusLine()
|
||||
|
||||
GetPanelState() IListPanelState
|
||||
|
||||
Context
|
||||
}
|
||||
|
||||
func (self *ListContext) GetPanelState() IListPanelState {
|
||||
return self.OnGetPanelState()
|
||||
}
|
||||
|
||||
type IListPanelState interface {
|
||||
SetSelectedLineIdx(int)
|
||||
GetSelectedLineIdx() int
|
||||
|
|
@ -29,12 +57,26 @@ type ListItem interface {
|
|||
Description() string
|
||||
}
|
||||
|
||||
func (lc *ListContext) GetSelectedItem() (ListItem, bool) {
|
||||
return lc.SelectedItem()
|
||||
func (self *ListContext) FocusLine() {
|
||||
view, err := self.Gui.g.View(self.ViewName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
view.FocusPoint(0, self.GetPanelState().GetSelectedLineIdx())
|
||||
view.Footer = formatListFooter(self.GetPanelState().GetSelectedLineIdx(), self.GetItemsLength())
|
||||
}
|
||||
|
||||
func (lc *ListContext) GetSelectedItemId() string {
|
||||
item, ok := lc.SelectedItem()
|
||||
func formatListFooter(selectedLineIdx int, length int) string {
|
||||
return fmt.Sprintf("%d of %d", selectedLineIdx+1, length)
|
||||
}
|
||||
|
||||
func (self *ListContext) GetSelectedItem() (ListItem, bool) {
|
||||
return self.SelectedItem()
|
||||
}
|
||||
|
||||
func (self *ListContext) GetSelectedItemId() string {
|
||||
item, ok := self.GetSelectedItem()
|
||||
|
||||
if !ok {
|
||||
return ""
|
||||
|
|
@ -44,145 +86,135 @@ func (lc *ListContext) GetSelectedItemId() string {
|
|||
}
|
||||
|
||||
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
|
||||
func (lc *ListContext) OnRender() error {
|
||||
view, err := lc.Gui.g.View(lc.ViewName)
|
||||
func (self *ListContext) OnRender() error {
|
||||
view, err := self.Gui.g.View(self.ViewName)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if lc.GetDisplayStrings != nil {
|
||||
lc.Gui.refreshSelectedLine(lc.GetPanelState(), lc.GetItemsLength())
|
||||
lc.Gui.renderDisplayStrings(view, lc.GetDisplayStrings())
|
||||
if self.GetDisplayStrings != nil {
|
||||
self.Gui.refreshSelectedLine(self.GetPanelState(), self.GetItemsLength())
|
||||
self.Gui.renderDisplayStrings(view, self.GetDisplayStrings(0, self.GetItemsLength()))
|
||||
self.Gui.render()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lc *ListContext) HandleFocusLost() error {
|
||||
if lc.OnFocusLost != nil {
|
||||
return lc.OnFocusLost()
|
||||
func (self *ListContext) HandleFocusLost() error {
|
||||
if self.OnFocusLost != nil {
|
||||
return self.OnFocusLost()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lc *ListContext) HandleFocus() error {
|
||||
if lc.Gui.popupPanelFocused() {
|
||||
func (self *ListContext) HandleFocus() error {
|
||||
if self.Gui.popupPanelFocused() {
|
||||
return nil
|
||||
}
|
||||
|
||||
view, err := lc.Gui.g.View(lc.ViewName)
|
||||
if err != nil {
|
||||
return nil
|
||||
self.FocusLine()
|
||||
|
||||
if self.Gui.State.Modes.Diffing.Active() {
|
||||
return self.Gui.renderDiff()
|
||||
}
|
||||
|
||||
view.FocusPoint(0, lc.GetPanelState().GetSelectedLineIdx())
|
||||
|
||||
if lc.Gui.State.Modes.Diffing.Active() {
|
||||
return lc.Gui.renderDiff()
|
||||
}
|
||||
|
||||
if lc.OnFocus != nil {
|
||||
return lc.OnFocus()
|
||||
if self.OnFocus != nil {
|
||||
return self.OnFocus()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lc *ListContext) HandleRender() error {
|
||||
return lc.OnRender()
|
||||
func (self *ListContext) HandleRender() error {
|
||||
return self.OnRender()
|
||||
}
|
||||
|
||||
func (lc *ListContext) handlePrevLine() error {
|
||||
return lc.handleLineChange(-1)
|
||||
func (self *ListContext) handlePrevLine() error {
|
||||
return self.handleLineChange(-1)
|
||||
}
|
||||
|
||||
func (lc *ListContext) handleNextLine() error {
|
||||
return lc.handleLineChange(1)
|
||||
func (self *ListContext) handleNextLine() error {
|
||||
return self.handleLineChange(1)
|
||||
}
|
||||
|
||||
func (lc *ListContext) handleLineChange(change int) error {
|
||||
if !lc.Gui.isPopupPanel(lc.ViewName) && lc.Gui.popupPanelFocused() {
|
||||
func (self *ListContext) handleLineChange(change int) error {
|
||||
if !self.Gui.isPopupPanel(self.ViewName) && self.Gui.popupPanelFocused() {
|
||||
return nil
|
||||
}
|
||||
|
||||
view, err := lc.Gui.g.View(lc.ViewName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selectedLineIdx := lc.GetPanelState().GetSelectedLineIdx()
|
||||
if (change < 0 && selectedLineIdx == 0) || (change > 0 && selectedLineIdx == lc.GetItemsLength()-1) {
|
||||
selectedLineIdx := self.GetPanelState().GetSelectedLineIdx()
|
||||
if (change < 0 && selectedLineIdx == 0) || (change > 0 && selectedLineIdx == self.GetItemsLength()-1) {
|
||||
return nil
|
||||
}
|
||||
|
||||
lc.Gui.changeSelectedLine(lc.GetPanelState(), lc.GetItemsLength(), change)
|
||||
view.FocusPoint(0, lc.GetPanelState().GetSelectedLineIdx())
|
||||
self.Gui.changeSelectedLine(self.GetPanelState(), self.GetItemsLength(), change)
|
||||
|
||||
return lc.HandleFocus()
|
||||
return self.HandleFocus()
|
||||
}
|
||||
|
||||
func (lc *ListContext) handleNextPage() error {
|
||||
view, err := lc.Gui.g.View(lc.ViewName)
|
||||
func (self *ListContext) handleNextPage() error {
|
||||
view, err := self.Gui.g.View(self.ViewName)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
delta := lc.Gui.pageDelta(view)
|
||||
delta := self.Gui.pageDelta(view)
|
||||
|
||||
return lc.handleLineChange(delta)
|
||||
return self.handleLineChange(delta)
|
||||
}
|
||||
|
||||
func (lc *ListContext) handleGotoTop() error {
|
||||
return lc.handleLineChange(-lc.GetItemsLength())
|
||||
func (self *ListContext) handleGotoTop() error {
|
||||
return self.handleLineChange(-self.GetItemsLength())
|
||||
}
|
||||
|
||||
func (lc *ListContext) handleGotoBottom() error {
|
||||
return lc.handleLineChange(lc.GetItemsLength())
|
||||
func (self *ListContext) handleGotoBottom() error {
|
||||
return self.handleLineChange(self.GetItemsLength())
|
||||
}
|
||||
|
||||
func (lc *ListContext) handlePrevPage() error {
|
||||
view, err := lc.Gui.g.View(lc.ViewName)
|
||||
func (self *ListContext) handlePrevPage() error {
|
||||
view, err := self.Gui.g.View(self.ViewName)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
delta := lc.Gui.pageDelta(view)
|
||||
delta := self.Gui.pageDelta(view)
|
||||
|
||||
return lc.handleLineChange(-delta)
|
||||
return self.handleLineChange(-delta)
|
||||
}
|
||||
|
||||
func (lc *ListContext) handleClick() error {
|
||||
if !lc.Gui.isPopupPanel(lc.ViewName) && lc.Gui.popupPanelFocused() {
|
||||
func (self *ListContext) handleClick() error {
|
||||
if !self.Gui.isPopupPanel(self.ViewName) && self.Gui.popupPanelFocused() {
|
||||
return nil
|
||||
}
|
||||
|
||||
view, err := lc.Gui.g.View(lc.ViewName)
|
||||
view, err := self.Gui.g.View(self.ViewName)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
prevSelectedLineIdx := lc.GetPanelState().GetSelectedLineIdx()
|
||||
prevSelectedLineIdx := self.GetPanelState().GetSelectedLineIdx()
|
||||
newSelectedLineIdx := view.SelectedLineIdx()
|
||||
|
||||
// we need to focus the view
|
||||
if err := lc.Gui.pushContext(lc); err != nil {
|
||||
if err := self.Gui.pushContext(self); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if newSelectedLineIdx > lc.GetItemsLength()-1 {
|
||||
if newSelectedLineIdx > self.GetItemsLength()-1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
lc.GetPanelState().SetSelectedLineIdx(newSelectedLineIdx)
|
||||
self.GetPanelState().SetSelectedLineIdx(newSelectedLineIdx)
|
||||
|
||||
prevViewName := lc.Gui.currentViewName()
|
||||
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lc.ViewName && lc.OnClickSelectedItem != nil {
|
||||
return lc.OnClickSelectedItem()
|
||||
prevViewName := self.Gui.currentViewName()
|
||||
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == self.ViewName && self.OnClickSelectedItem != nil {
|
||||
return self.OnClickSelectedItem()
|
||||
}
|
||||
return lc.HandleFocus()
|
||||
return self.HandleFocus()
|
||||
}
|
||||
|
||||
func (lc *ListContext) onSearchSelect(selectedLineIdx int) error {
|
||||
lc.GetPanelState().SetSelectedLineIdx(selectedLineIdx)
|
||||
return lc.HandleFocus()
|
||||
func (self *ListContext) onSearchSelect(selectedLineIdx int) error {
|
||||
self.GetPanelState().SetSelectedLineIdx(selectedLineIdx)
|
||||
return self.HandleFocus()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
func (gui *Gui) menuListContext() *ListContext {
|
||||
func (gui *Gui) menuListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "menu",
|
||||
|
|
@ -15,7 +15,7 @@ func (gui *Gui) menuListContext() *ListContext {
|
|||
OnGetOptionsMap: gui.getMenuOptions,
|
||||
},
|
||||
GetItemsLength: func() int { return gui.Views.Menu.LinesHeight() },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Menu },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Menu },
|
||||
OnFocus: gui.handleMenuSelect,
|
||||
OnClickSelectedItem: gui.onMenuPress,
|
||||
Gui: gui,
|
||||
|
|
@ -24,7 +24,7 @@ func (gui *Gui) menuListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) filesListContext() *ListContext {
|
||||
func (gui *Gui) filesListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "files",
|
||||
|
|
@ -33,11 +33,11 @@ func (gui *Gui) filesListContext() *ListContext {
|
|||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return gui.State.FileManager.GetItemsLength() },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Files },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Files },
|
||||
OnFocus: gui.focusAndSelectFile,
|
||||
OnClickSelectedItem: gui.handleFilePress,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
lines := gui.State.FileManager.Render(gui.State.Modes.Diffing.Ref, gui.State.Submodules)
|
||||
mappedLines := make([][]string, len(lines))
|
||||
for i, line := range lines {
|
||||
|
|
@ -53,7 +53,7 @@ func (gui *Gui) filesListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) branchesListContext() *ListContext {
|
||||
func (gui *Gui) branchesListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "branches",
|
||||
|
|
@ -61,19 +61,13 @@ func (gui *Gui) branchesListContext() *ListContext {
|
|||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.Branches) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Branches },
|
||||
OnFocus: gui.handleBranchSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return len(gui.State.Branches) },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Branches },
|
||||
OnFocus: gui.handleBranchSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
prs := gui.GitCommand.GenerateGithubPullRequestMap(gui.State.GithubState.RecentPRs, gui.State.Branches)
|
||||
|
||||
return presentation.GetBranchListDisplayStrings(
|
||||
gui.State.Branches,
|
||||
prs,
|
||||
gui.State.ScreenMode != SCREEN_NORMAL,
|
||||
gui.State.Modes.Diffing.Ref,
|
||||
)
|
||||
return presentation.GetBranchListDisplayStrings(gui.State.Branches, prs, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
item := gui.getSelectedBranch()
|
||||
|
|
@ -82,7 +76,7 @@ func (gui *Gui) branchesListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) remotesListContext() *ListContext {
|
||||
func (gui *Gui) remotesListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "branches",
|
||||
|
|
@ -91,11 +85,11 @@ func (gui *Gui) remotesListContext() *ListContext {
|
|||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.Remotes) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Remotes },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Remotes },
|
||||
OnFocus: gui.handleRemoteSelect,
|
||||
OnClickSelectedItem: gui.handleRemoteEnter,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetRemoteListDisplayStrings(gui.State.Remotes, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
|
|
@ -105,7 +99,7 @@ func (gui *Gui) remotesListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) remoteBranchesListContext() *ListContext {
|
||||
func (gui *Gui) remoteBranchesListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "branches",
|
||||
|
|
@ -113,11 +107,11 @@ func (gui *Gui) remoteBranchesListContext() *ListContext {
|
|||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.RemoteBranches },
|
||||
OnFocus: gui.handleRemoteBranchSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.RemoteBranches },
|
||||
OnFocus: gui.handleRemoteBranchSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetRemoteBranchListDisplayStrings(gui.State.RemoteBranches, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
|
|
@ -127,7 +121,7 @@ func (gui *Gui) remoteBranchesListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) tagsListContext() *ListContext {
|
||||
func (gui *Gui) tagsListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "branches",
|
||||
|
|
@ -135,11 +129,11 @@ func (gui *Gui) tagsListContext() *ListContext {
|
|||
Key: TAGS_CONTEXT_KEY,
|
||||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.Tags) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Tags },
|
||||
OnFocus: gui.handleTagSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return len(gui.State.Tags) },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Tags },
|
||||
OnFocus: gui.handleTagSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetTagListDisplayStrings(gui.State.Tags, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
|
|
@ -149,7 +143,7 @@ func (gui *Gui) tagsListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) branchCommitsListContext() *ListContext {
|
||||
func (gui *Gui) branchCommitsListContext() IListContext {
|
||||
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
|
|
@ -159,11 +153,11 @@ func (gui *Gui) branchCommitsListContext() *ListContext {
|
|||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.Commits) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Commits },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Commits },
|
||||
OnFocus: gui.handleCommitSelect,
|
||||
OnClickSelectedItem: gui.handleViewCommitFiles,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
gui.State.Commits,
|
||||
gui.State.ScreenMode != SCREEN_NORMAL,
|
||||
|
|
@ -179,7 +173,7 @@ func (gui *Gui) branchCommitsListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) reflogCommitsListContext() *ListContext {
|
||||
func (gui *Gui) reflogCommitsListContext() IListContext {
|
||||
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
|
|
@ -188,11 +182,11 @@ func (gui *Gui) reflogCommitsListContext() *ListContext {
|
|||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.ReflogCommits },
|
||||
OnFocus: gui.handleReflogCommitSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.ReflogCommits },
|
||||
OnFocus: gui.handleReflogCommitSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetReflogCommitListDisplayStrings(
|
||||
gui.State.FilteredReflogCommits,
|
||||
gui.State.ScreenMode != SCREEN_NORMAL,
|
||||
|
|
@ -208,7 +202,7 @@ func (gui *Gui) reflogCommitsListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) subCommitsListContext() *ListContext {
|
||||
func (gui *Gui) subCommitsListContext() IListContext {
|
||||
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
|
|
@ -217,11 +211,11 @@ func (gui *Gui) subCommitsListContext() *ListContext {
|
|||
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.SubCommits) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.SubCommits },
|
||||
OnFocus: gui.handleSubCommitSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return len(gui.State.SubCommits) },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.SubCommits },
|
||||
OnFocus: gui.handleSubCommitSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
gui.State.SubCommits,
|
||||
gui.State.ScreenMode != SCREEN_NORMAL,
|
||||
|
|
@ -237,7 +231,7 @@ func (gui *Gui) subCommitsListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) stashListContext() *ListContext {
|
||||
func (gui *Gui) stashListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "stash",
|
||||
|
|
@ -245,11 +239,11 @@ func (gui *Gui) stashListContext() *ListContext {
|
|||
Key: STASH_CONTEXT_KEY,
|
||||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.StashEntries) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Stash },
|
||||
OnFocus: gui.handleStashEntrySelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return len(gui.State.StashEntries) },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Stash },
|
||||
OnFocus: gui.handleStashEntrySelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetStashEntryListDisplayStrings(gui.State.StashEntries, gui.State.Modes.Diffing.Ref)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
|
|
@ -259,7 +253,7 @@ func (gui *Gui) stashListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) commitFilesListContext() *ListContext {
|
||||
func (gui *Gui) commitFilesListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "commitFiles",
|
||||
|
|
@ -267,11 +261,11 @@ func (gui *Gui) commitFilesListContext() *ListContext {
|
|||
Key: COMMIT_FILES_CONTEXT_KEY,
|
||||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return gui.State.CommitFileManager.GetItemsLength() },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.CommitFiles },
|
||||
OnFocus: gui.handleCommitFileSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return gui.State.CommitFileManager.GetItemsLength() },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.CommitFiles },
|
||||
OnFocus: gui.handleCommitFileSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
if gui.State.CommitFileManager.GetItemsLength() == 0 {
|
||||
return [][]string{{style.FgRed.Sprint("(none)")}}
|
||||
}
|
||||
|
|
@ -291,7 +285,7 @@ func (gui *Gui) commitFilesListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) submodulesListContext() *ListContext {
|
||||
func (gui *Gui) submodulesListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "files",
|
||||
|
|
@ -299,11 +293,11 @@ func (gui *Gui) submodulesListContext() *ListContext {
|
|||
Key: SUBMODULES_CONTEXT_KEY,
|
||||
Kind: SIDE_CONTEXT,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.Submodules) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Submodules },
|
||||
OnFocus: gui.handleSubmoduleSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return len(gui.State.Submodules) },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Submodules },
|
||||
OnFocus: gui.handleSubmoduleSelect,
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetSubmoduleListDisplayStrings(gui.State.Submodules)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
|
|
@ -313,7 +307,7 @@ func (gui *Gui) submodulesListContext() *ListContext {
|
|||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) suggestionsListContext() *ListContext {
|
||||
func (gui *Gui) suggestionsListContext() IListContext {
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "suggestions",
|
||||
|
|
@ -321,18 +315,18 @@ func (gui *Gui) suggestionsListContext() *ListContext {
|
|||
Key: SUGGESTIONS_CONTEXT_KEY,
|
||||
Kind: PERSISTENT_POPUP,
|
||||
},
|
||||
GetItemsLength: func() int { return len(gui.State.Suggestions) },
|
||||
GetPanelState: func() IListPanelState { return gui.State.Panels.Suggestions },
|
||||
OnFocus: func() error { return nil },
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
GetItemsLength: func() int { return len(gui.State.Suggestions) },
|
||||
OnGetPanelState: func() IListPanelState { return gui.State.Panels.Suggestions },
|
||||
OnFocus: func() error { return nil },
|
||||
Gui: gui,
|
||||
GetDisplayStrings: func(startIdx int, length int) [][]string {
|
||||
return presentation.GetSuggestionListDisplayStrings(gui.State.Suggestions)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) getListContexts() []*ListContext {
|
||||
return []*ListContext{
|
||||
func (gui *Gui) getListContexts() []IListContext {
|
||||
return []IListContext{
|
||||
gui.State.Contexts.Menu,
|
||||
gui.State.Contexts.Files,
|
||||
gui.State.Contexts.Branches,
|
||||
|
|
@ -340,7 +334,6 @@ func (gui *Gui) getListContexts() []*ListContext {
|
|||
gui.State.Contexts.RemoteBranches,
|
||||
gui.State.Contexts.Tags,
|
||||
gui.State.Contexts.BranchCommits,
|
||||
gui.State.Contexts.BranchCommits,
|
||||
gui.State.Contexts.ReflogCommits,
|
||||
gui.State.Contexts.SubCommits,
|
||||
gui.State.Contexts.Stash,
|
||||
|
|
@ -359,38 +352,38 @@ func (gui *Gui) getListContextKeyBindings() []*Binding {
|
|||
listContext := listContext
|
||||
|
||||
bindings = append(bindings, []*Binding{
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gui.getKey(keybindingConfig.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gui.getKey(keybindingConfig.Universal.PrevItem), Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gui.getKey(keybindingConfig.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gui.getKey(keybindingConfig.Universal.NextItem), Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gui.getKey(keybindingConfig.Universal.PrevPage), Modifier: gocui.ModNone, Handler: listContext.handlePrevPage, Description: gui.Tr.LcPrevPage},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gui.getKey(keybindingConfig.Universal.NextPage), Modifier: gocui.ModNone, Handler: listContext.handleNextPage, Description: gui.Tr.LcNextPage},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gui.getKey(keybindingConfig.Universal.GotoTop), Modifier: gocui.ModNone, Handler: listContext.handleGotoTop, Description: gui.Tr.LcGotoTop},
|
||||
{ViewName: listContext.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.ViewName, Contexts: []string{string(listContext.Key)}, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: listContext.handleClick},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.PrevItemAlt), Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.PrevItem), Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: listContext.handlePrevLine},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.NextItemAlt), Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.NextItem), Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.PrevPage), Modifier: gocui.ModNone, Handler: listContext.handlePrevPage, Description: gui.Tr.LcPrevPage},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.NextPage), Modifier: gocui.ModNone, Handler: listContext.handleNextPage, Description: gui.Tr.LcNextPage},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gui.getKey(keybindingConfig.Universal.GotoTop), Modifier: gocui.ModNone, Handler: listContext.handleGotoTop, Description: gui.Tr.LcGotoTop},
|
||||
{ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listContext.handleNextLine},
|
||||
{ViewName: listContext.GetViewName(), Contexts: []string{string(listContext.GetKey())}, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: listContext.handleClick},
|
||||
}...)
|
||||
|
||||
// the commits panel needs to lazyload things so it has a couple of its own handlers
|
||||
openSearchHandler := gui.handleOpenSearch
|
||||
gotoBottomHandler := listContext.handleGotoBottom
|
||||
if listContext.ViewName == "commits" {
|
||||
if listContext.GetViewName() == "commits" {
|
||||
openSearchHandler = gui.handleOpenSearchForCommitsPanel
|
||||
gotoBottomHandler = gui.handleGotoBottomForCommitsPanel
|
||||
}
|
||||
|
||||
bindings = append(bindings, []*Binding{
|
||||
{
|
||||
ViewName: listContext.ViewName,
|
||||
Contexts: []string{string(listContext.Key)},
|
||||
ViewName: listContext.GetViewName(),
|
||||
Contexts: []string{string(listContext.GetKey())},
|
||||
Key: gui.getKey(keybindingConfig.Universal.StartSearch),
|
||||
Handler: func() error { return openSearchHandler(listContext.ViewName) },
|
||||
Handler: func() error { return openSearchHandler(listContext.GetViewName()) },
|
||||
Description: gui.Tr.LcStartSearch,
|
||||
Tag: "navigation",
|
||||
},
|
||||
{
|
||||
ViewName: listContext.ViewName,
|
||||
Contexts: []string{string(listContext.Key)},
|
||||
ViewName: listContext.GetViewName(),
|
||||
Contexts: []string{string(listContext.GetKey())},
|
||||
Key: gui.getKey(keybindingConfig.Universal.GotoBottom),
|
||||
Handler: gotoBottomHandler,
|
||||
Description: gui.Tr.LcGotoBottom,
|
||||
|
|
|
|||
|
|
@ -78,12 +78,10 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr
|
|||
menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
|
||||
menuView.Title = title
|
||||
menuView.FgColor = theme.GocuiDefaultTextColor
|
||||
menuView.ContainsList = true
|
||||
menuView.Clear()
|
||||
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
|
||||
return nil
|
||||
}))
|
||||
fmt.Fprint(menuView, list)
|
||||
menuView.SetContent(list)
|
||||
gui.State.Panels.Menu.SelectedLineIdx = 0
|
||||
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ func (gui *Gui) promptToContinueRebase() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return gui.genericMergeCommand("continue")
|
||||
return gui.genericMergeCommand(REBASE_OPTION_CONTINUE)
|
||||
},
|
||||
handleClose: func() error {
|
||||
return gui.pushContext(gui.State.Contexts.Files)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
|
|
@ -58,5 +59,19 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
|||
},
|
||||
reset: gui.exitCherryPickingMode,
|
||||
},
|
||||
{
|
||||
isActive: func() bool {
|
||||
return gui.GitCommand.WorkingTreeState() != commands.REBASE_MODE_NORMAL
|
||||
},
|
||||
description: func() string {
|
||||
workingTreeState := gui.GitCommand.WorkingTreeState()
|
||||
return style.FgYellow.Sprintf(
|
||||
"%s %s",
|
||||
workingTreeState,
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
)
|
||||
},
|
||||
reset: gui.abortMergeOrRebaseWithConfirm,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
141
pkg/gui/presentation/graph/cell.go
Normal file
141
pkg/gui/presentation/graph/cell.go
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
package graph
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
const mergeSymbol = '⏣'
|
||||
const commitSymbol = '⎔'
|
||||
|
||||
type cellType int
|
||||
|
||||
const (
|
||||
CONNECTION cellType = iota
|
||||
COMMIT
|
||||
MERGE
|
||||
)
|
||||
|
||||
type Cell struct {
|
||||
up, down, left, right bool
|
||||
cellType cellType
|
||||
rightStyle *style.TextStyle
|
||||
style style.TextStyle
|
||||
}
|
||||
|
||||
func (cell *Cell) render() string {
|
||||
up, down, left, right := cell.up, cell.down, cell.left, cell.right
|
||||
|
||||
first, second := getBoxDrawingChars(up, down, left, right)
|
||||
var adjustedFirst rune
|
||||
switch cell.cellType {
|
||||
case CONNECTION:
|
||||
adjustedFirst = first
|
||||
case COMMIT:
|
||||
adjustedFirst = commitSymbol
|
||||
case MERGE:
|
||||
adjustedFirst = mergeSymbol
|
||||
}
|
||||
|
||||
var rightStyle *style.TextStyle
|
||||
if cell.rightStyle == nil {
|
||||
rightStyle = &cell.style
|
||||
} else {
|
||||
rightStyle = cell.rightStyle
|
||||
}
|
||||
|
||||
// just doing this for the sake of easy testing, so that we don't need to
|
||||
// assert on the style of a space given a space has no styling (assuming we
|
||||
// stick to only using foreground styles)
|
||||
var styledSecondChar string
|
||||
if second == ' ' {
|
||||
styledSecondChar = " "
|
||||
} else {
|
||||
styledSecondChar = rightStyle.Sprint(string(second))
|
||||
}
|
||||
|
||||
return cell.style.Sprint(string(adjustedFirst)) + styledSecondChar
|
||||
}
|
||||
|
||||
func (cell *Cell) reset() {
|
||||
cell.up = false
|
||||
cell.down = false
|
||||
cell.left = false
|
||||
cell.right = false
|
||||
}
|
||||
|
||||
func (cell *Cell) setUp(style style.TextStyle) *Cell {
|
||||
cell.up = true
|
||||
cell.style = style
|
||||
return cell
|
||||
}
|
||||
|
||||
func (cell *Cell) setDown(style style.TextStyle) *Cell {
|
||||
cell.down = true
|
||||
cell.style = style
|
||||
return cell
|
||||
}
|
||||
|
||||
func (cell *Cell) setLeft(style style.TextStyle) *Cell {
|
||||
cell.left = true
|
||||
if !cell.up && !cell.down {
|
||||
// vertical trumps left
|
||||
cell.style = style
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
func (cell *Cell) setRight(style style.TextStyle, override bool) *Cell {
|
||||
cell.right = true
|
||||
if cell.rightStyle == nil || override {
|
||||
cell.rightStyle = &style
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
func (cell *Cell) setStyle(style style.TextStyle) *Cell {
|
||||
cell.style = style
|
||||
return cell
|
||||
}
|
||||
|
||||
func (cell *Cell) setType(cellType cellType) *Cell {
|
||||
cell.cellType = cellType
|
||||
return cell
|
||||
}
|
||||
|
||||
func getBoxDrawingChars(up, down, left, right bool) (rune, rune) {
|
||||
if up && down && left && right {
|
||||
return '│', '─'
|
||||
} else if up && down && left && !right {
|
||||
return '│', ' '
|
||||
} else if up && down && !left && right {
|
||||
return '│', '─'
|
||||
} else if up && down && !left && !right {
|
||||
return '│', ' '
|
||||
} else if up && !down && left && right {
|
||||
return '┴', '─'
|
||||
} else if up && !down && left && !right {
|
||||
return '╯', ' '
|
||||
} else if up && !down && !left && right {
|
||||
return '╰', '─'
|
||||
} else if up && !down && !left && !right {
|
||||
return '╵', ' '
|
||||
} else if !up && down && left && right {
|
||||
return '┬', '─'
|
||||
} else if !up && down && left && !right {
|
||||
return '╮', ' '
|
||||
} else if !up && down && !left && right {
|
||||
return '╭', '─'
|
||||
} else if !up && down && !left && !right {
|
||||
return '╷', ' '
|
||||
} else if !up && !down && left && right {
|
||||
return '─', '─'
|
||||
} else if !up && !down && left && !right {
|
||||
return '─', ' '
|
||||
} else if !up && !down && !left && right {
|
||||
return '╶', '─'
|
||||
} else if !up && !down && !left && !right {
|
||||
return ' ', ' '
|
||||
} else {
|
||||
panic("should not be possible")
|
||||
}
|
||||
}
|
||||
375
pkg/gui/presentation/graph/graph.go
Normal file
375
pkg/gui/presentation/graph/graph.go
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
package graph
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type PipeKind uint8
|
||||
|
||||
const (
|
||||
TERMINATES PipeKind = iota
|
||||
STARTS
|
||||
CONTINUES
|
||||
)
|
||||
|
||||
type Pipe struct {
|
||||
fromPos int
|
||||
toPos int
|
||||
fromSha string
|
||||
toSha string
|
||||
kind PipeKind
|
||||
style style.TextStyle
|
||||
}
|
||||
|
||||
var highlightStyle = style.FgLightWhite.SetBold()
|
||||
|
||||
func ContainsCommitSha(pipes []Pipe, sha string) bool {
|
||||
for _, pipe := range pipes {
|
||||
if equalHashes(pipe.fromSha, sha) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self Pipe) left() int {
|
||||
return utils.Min(self.fromPos, self.toPos)
|
||||
}
|
||||
|
||||
func (self Pipe) right() int {
|
||||
return utils.Max(self.fromPos, self.toPos)
|
||||
}
|
||||
|
||||
func RenderCommitGraph(commits []*models.Commit, selectedCommitSha string, getStyle func(c *models.Commit) style.TextStyle) []string {
|
||||
pipeSets := GetPipeSets(commits, getStyle)
|
||||
if len(pipeSets) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
lines := RenderAux(pipeSets, commits, selectedCommitSha)
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func GetPipeSets(commits []*models.Commit, getStyle func(c *models.Commit) style.TextStyle) [][]Pipe {
|
||||
if len(commits) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
pipes := []Pipe{{fromPos: 0, toPos: 0, fromSha: "START", toSha: commits[0].Sha, kind: STARTS, style: style.FgDefault}}
|
||||
|
||||
pipeSets := [][]Pipe{}
|
||||
for _, commit := range commits {
|
||||
pipes = getNextPipes(pipes, commit, getStyle)
|
||||
pipeSets = append(pipeSets, pipes)
|
||||
}
|
||||
|
||||
return pipeSets
|
||||
}
|
||||
|
||||
func RenderAux(pipeSets [][]Pipe, commits []*models.Commit, selectedCommitSha string) []string {
|
||||
lines := make([]string, len(pipeSets))
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(pipeSets))
|
||||
for i, pipeSet := range pipeSets {
|
||||
i := i
|
||||
pipeSet := pipeSet
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var prevCommit *models.Commit
|
||||
if i > 0 {
|
||||
prevCommit = commits[i-1]
|
||||
}
|
||||
line := renderPipeSet(pipeSet, selectedCommitSha, prevCommit)
|
||||
lines[i] = line
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
return lines
|
||||
}
|
||||
|
||||
func getNextPipes(prevPipes []Pipe, commit *models.Commit, getStyle func(c *models.Commit) style.TextStyle) []Pipe {
|
||||
currentPipes := make([]Pipe, 0, len(prevPipes))
|
||||
maxPos := 0
|
||||
for _, pipe := range prevPipes {
|
||||
// a pipe that terminated in the previous line has no bearing on the current line
|
||||
// so we'll filter those out
|
||||
if pipe.kind != TERMINATES {
|
||||
currentPipes = append(currentPipes, pipe)
|
||||
}
|
||||
maxPos = utils.Max(maxPos, pipe.toPos)
|
||||
}
|
||||
|
||||
newPipes := make([]Pipe, 0, len(currentPipes)+len(commit.Parents))
|
||||
// start by assuming that we've got a brand new commit not related to any preceding commit.
|
||||
// (this only happens when we're doing `git log --all`). These will be tacked onto the far end.
|
||||
pos := maxPos + 1
|
||||
for _, pipe := range currentPipes {
|
||||
if equalHashes(pipe.toSha, commit.Sha) {
|
||||
// turns out this commit does have a descendant so we'll place it right under the first instance
|
||||
pos = pipe.toPos
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// a taken spot is one where a current pipe is ending on
|
||||
takenSpots := make(map[int]bool)
|
||||
// a traversed spot is one where a current pipe is starting on, ending on, or passing through
|
||||
traversedSpots := make(map[int]bool)
|
||||
|
||||
if len(commit.Parents) > 0 {
|
||||
newPipes = append(newPipes, Pipe{
|
||||
fromPos: pos,
|
||||
toPos: pos,
|
||||
fromSha: commit.Sha,
|
||||
toSha: commit.Parents[0],
|
||||
kind: STARTS,
|
||||
style: getStyle(commit),
|
||||
})
|
||||
}
|
||||
|
||||
traversedSpotsForContinuingPipes := make(map[int]bool)
|
||||
for _, pipe := range currentPipes {
|
||||
if !equalHashes(pipe.toSha, commit.Sha) {
|
||||
traversedSpotsForContinuingPipes[pipe.toPos] = true
|
||||
}
|
||||
}
|
||||
|
||||
getNextAvailablePosForContinuingPipe := func() int {
|
||||
i := 0
|
||||
for {
|
||||
if !traversedSpots[i] {
|
||||
return i
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
getNextAvailablePosForNewPipe := func() int {
|
||||
i := 0
|
||||
for {
|
||||
// a newly created pipe is not allowed to end on a spot that's already taken,
|
||||
// nor on a spot that's been traversed by a continuing pipe.
|
||||
if !takenSpots[i] && !traversedSpotsForContinuingPipes[i] {
|
||||
return i
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
traverse := func(from, to int) {
|
||||
left, right := from, to
|
||||
if left > right {
|
||||
left, right = right, left
|
||||
}
|
||||
for i := left; i <= right; i++ {
|
||||
traversedSpots[i] = true
|
||||
}
|
||||
takenSpots[to] = true
|
||||
}
|
||||
|
||||
for _, pipe := range currentPipes {
|
||||
if equalHashes(pipe.toSha, commit.Sha) {
|
||||
// terminating here
|
||||
newPipes = append(newPipes, Pipe{
|
||||
fromPos: pipe.toPos,
|
||||
toPos: pos,
|
||||
fromSha: pipe.fromSha,
|
||||
toSha: pipe.toSha,
|
||||
kind: TERMINATES,
|
||||
style: pipe.style,
|
||||
})
|
||||
traverse(pipe.toPos, pos)
|
||||
} else if pipe.toPos < pos {
|
||||
// continuing here
|
||||
availablePos := getNextAvailablePosForContinuingPipe()
|
||||
newPipes = append(newPipes, Pipe{
|
||||
fromPos: pipe.toPos,
|
||||
toPos: availablePos,
|
||||
fromSha: pipe.fromSha,
|
||||
toSha: pipe.toSha,
|
||||
kind: CONTINUES,
|
||||
style: pipe.style,
|
||||
})
|
||||
traverse(pipe.toPos, availablePos)
|
||||
}
|
||||
}
|
||||
|
||||
if commit.IsMerge() {
|
||||
for _, parent := range commit.Parents[1:] {
|
||||
availablePos := getNextAvailablePosForNewPipe()
|
||||
// need to act as if continuing pipes are going to continue on the same line.
|
||||
newPipes = append(newPipes, Pipe{
|
||||
fromPos: pos,
|
||||
toPos: availablePos,
|
||||
fromSha: commit.Sha,
|
||||
toSha: parent,
|
||||
kind: STARTS,
|
||||
style: getStyle(commit),
|
||||
})
|
||||
|
||||
takenSpots[availablePos] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, pipe := range currentPipes {
|
||||
if !equalHashes(pipe.toSha, commit.Sha) && pipe.toPos > pos {
|
||||
// continuing on, potentially moving left to fill in a blank spot
|
||||
last := pipe.toPos
|
||||
for i := pipe.toPos; i > pos; i-- {
|
||||
if takenSpots[i] || traversedSpots[i] {
|
||||
break
|
||||
} else {
|
||||
last = i
|
||||
}
|
||||
}
|
||||
newPipes = append(newPipes, Pipe{
|
||||
fromPos: pipe.toPos,
|
||||
toPos: last,
|
||||
fromSha: pipe.fromSha,
|
||||
toSha: pipe.toSha,
|
||||
kind: CONTINUES,
|
||||
style: pipe.style,
|
||||
})
|
||||
traverse(pipe.toPos, last)
|
||||
}
|
||||
}
|
||||
|
||||
// not efficient but doing it for now: sorting my pipes by toPos, then by kind
|
||||
sort.Slice(newPipes, func(i, j int) bool {
|
||||
if newPipes[i].toPos == newPipes[j].toPos {
|
||||
return newPipes[i].kind < newPipes[j].kind
|
||||
}
|
||||
return newPipes[i].toPos < newPipes[j].toPos
|
||||
})
|
||||
|
||||
return newPipes
|
||||
}
|
||||
|
||||
func renderPipeSet(
|
||||
pipes []Pipe,
|
||||
selectedCommitSha string,
|
||||
prevCommit *models.Commit,
|
||||
) string {
|
||||
maxPos := 0
|
||||
commitPos := 0
|
||||
startCount := 0
|
||||
for _, pipe := range pipes {
|
||||
if pipe.kind == STARTS {
|
||||
startCount++
|
||||
commitPos = pipe.fromPos
|
||||
} else if pipe.kind == TERMINATES {
|
||||
commitPos = pipe.toPos
|
||||
}
|
||||
|
||||
if pipe.right() > maxPos {
|
||||
maxPos = pipe.right()
|
||||
}
|
||||
}
|
||||
isMerge := startCount > 1
|
||||
|
||||
cells := make([]*Cell, maxPos+1)
|
||||
for i := range cells {
|
||||
cells[i] = &Cell{cellType: CONNECTION, style: style.FgDefault}
|
||||
}
|
||||
|
||||
renderPipe := func(pipe Pipe, style style.TextStyle, overrideRightStyle bool) {
|
||||
left := pipe.left()
|
||||
right := pipe.right()
|
||||
|
||||
if left != right {
|
||||
for i := left + 1; i < right; i++ {
|
||||
cells[i].setLeft(style).setRight(style, overrideRightStyle)
|
||||
}
|
||||
cells[left].setRight(style, overrideRightStyle)
|
||||
cells[right].setLeft(style)
|
||||
}
|
||||
|
||||
if pipe.kind == STARTS || pipe.kind == CONTINUES {
|
||||
cells[pipe.toPos].setDown(style)
|
||||
}
|
||||
if pipe.kind == TERMINATES || pipe.kind == CONTINUES {
|
||||
cells[pipe.fromPos].setUp(style)
|
||||
}
|
||||
}
|
||||
|
||||
// we don't want to highlight two commits if they're contiguous. We only want
|
||||
// to highlight multiple things if there's an actual visible pipe involved.
|
||||
highlight := true
|
||||
if prevCommit != nil && equalHashes(prevCommit.Sha, selectedCommitSha) {
|
||||
highlight = false
|
||||
for _, pipe := range pipes {
|
||||
if equalHashes(pipe.fromSha, selectedCommitSha) && (pipe.kind != TERMINATES || pipe.fromPos != pipe.toPos) {
|
||||
highlight = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// so we have our commit pos again, now it's time to build the cells.
|
||||
// we'll handle the one that's sourced from our selected commit last so that it can override the other cells.
|
||||
selectedPipes := []Pipe{}
|
||||
// pre-allocating this one because most of the time we'll only have non-selected pipes
|
||||
nonSelectedPipes := make([]Pipe, 0, len(pipes))
|
||||
|
||||
for _, pipe := range pipes {
|
||||
if highlight && equalHashes(pipe.fromSha, selectedCommitSha) {
|
||||
selectedPipes = append(selectedPipes, pipe)
|
||||
} else {
|
||||
nonSelectedPipes = append(nonSelectedPipes, pipe)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pipe := range nonSelectedPipes {
|
||||
if pipe.kind == STARTS {
|
||||
renderPipe(pipe, pipe.style, true)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pipe := range nonSelectedPipes {
|
||||
if pipe.kind != STARTS && !(pipe.kind == TERMINATES && pipe.fromPos == commitPos && pipe.toPos == commitPos) {
|
||||
renderPipe(pipe, pipe.style, false)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pipe := range selectedPipes {
|
||||
for i := pipe.left(); i <= pipe.right(); i++ {
|
||||
cells[i].reset()
|
||||
}
|
||||
}
|
||||
for _, pipe := range selectedPipes {
|
||||
renderPipe(pipe, highlightStyle, true)
|
||||
if pipe.toPos == commitPos {
|
||||
cells[pipe.toPos].setStyle(highlightStyle)
|
||||
}
|
||||
}
|
||||
|
||||
cType := COMMIT
|
||||
if isMerge {
|
||||
cType = MERGE
|
||||
}
|
||||
|
||||
cells[commitPos].setType(cType)
|
||||
|
||||
renderedCells := make([]string, len(cells))
|
||||
for i, cell := range cells {
|
||||
renderedCells[i] = cell.render()
|
||||
}
|
||||
return strings.Join(renderedCells, "")
|
||||
}
|
||||
|
||||
func equalHashes(a, b string) bool {
|
||||
// if our selectedCommitSha is an empty string we treat that as meaning there is no selected commit sha
|
||||
if a == "" || b == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
length := utils.Min(len(a), len(b))
|
||||
// parent hashes are only stored up to 20 characters for some reason so we'll truncate to that for comparison
|
||||
return a[:length] == b[:length]
|
||||
}
|
||||
553
pkg/gui/presentation/graph/graph_test.go
Normal file
553
pkg/gui/presentation/graph/graph_test.go
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
package graph
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xo/terminfo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// on CI we've got no color capability so we're forcing it here
|
||||
color.ForceSetColorLevel(terminfo.ColorLevelMillions)
|
||||
}
|
||||
|
||||
func TestRenderCommitGraph(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
commits []*models.Commit
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
name: "with some merges",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"3"}},
|
||||
{Sha: "3", Parents: []string{"4"}},
|
||||
{Sha: "4", Parents: []string{"5", "7"}},
|
||||
{Sha: "7", Parents: []string{"5"}},
|
||||
{Sha: "5", Parents: []string{"8"}},
|
||||
{Sha: "8", Parents: []string{"9"}},
|
||||
{Sha: "9", Parents: []string{"A", "B"}},
|
||||
{Sha: "B", Parents: []string{"D"}},
|
||||
{Sha: "D", Parents: []string{"D"}},
|
||||
{Sha: "A", Parents: []string{"E"}},
|
||||
{Sha: "E", Parents: []string{"F"}},
|
||||
{Sha: "F", Parents: []string{"D"}},
|
||||
{Sha: "D", Parents: []string{"G"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⎔
|
||||
2 ⎔
|
||||
3 ⎔
|
||||
4 ⏣─╮
|
||||
7 │ ⎔
|
||||
5 ⎔─╯
|
||||
8 ⎔
|
||||
9 ⏣─╮
|
||||
B │ ⎔
|
||||
D │ ⎔
|
||||
A ⎔ │
|
||||
E ⎔ │
|
||||
F ⎔ │
|
||||
D ⎔─╯`,
|
||||
},
|
||||
{
|
||||
name: "with a path that has room to move to the left",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"3", "4"}},
|
||||
{Sha: "4", Parents: []string{"3", "5"}},
|
||||
{Sha: "3", Parents: []string{"5"}},
|
||||
{Sha: "5", Parents: []string{"6"}},
|
||||
{Sha: "6", Parents: []string{"7"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⎔
|
||||
2 ⏣─╮
|
||||
4 │ ⏣─╮
|
||||
3 ⎔─╯ │
|
||||
5 ⎔───╯
|
||||
6 ⎔`,
|
||||
},
|
||||
{
|
||||
name: "with a new commit",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"3", "4"}},
|
||||
{Sha: "4", Parents: []string{"3", "5"}},
|
||||
{Sha: "Z", Parents: []string{"Z"}},
|
||||
{Sha: "3", Parents: []string{"5"}},
|
||||
{Sha: "5", Parents: []string{"6"}},
|
||||
{Sha: "6", Parents: []string{"7"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⎔
|
||||
2 ⏣─╮
|
||||
4 │ ⏣─╮
|
||||
Z │ │ │ ⎔
|
||||
3 ⎔─╯ │ │
|
||||
5 ⎔───╯ │
|
||||
6 ⎔ ╭───╯`,
|
||||
},
|
||||
{
|
||||
name: "with a path that has room to move to the left and continues",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"3", "4"}},
|
||||
{Sha: "3", Parents: []string{"5", "4"}},
|
||||
{Sha: "5", Parents: []string{"7", "8"}},
|
||||
{Sha: "4", Parents: []string{"7"}},
|
||||
{Sha: "7", Parents: []string{"11"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⎔
|
||||
2 ⏣─╮
|
||||
3 ⏣─│─╮
|
||||
5 ⏣─│─│─╮
|
||||
4 │ ⎔─╯ │
|
||||
7 ⎔─╯ ╭─╯`,
|
||||
},
|
||||
{
|
||||
name: "with a path that has room to move to the left and continues",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"3", "4"}},
|
||||
{Sha: "3", Parents: []string{"5", "4"}},
|
||||
{Sha: "5", Parents: []string{"7", "8"}},
|
||||
{Sha: "7", Parents: []string{"4", "A"}},
|
||||
{Sha: "4", Parents: []string{"B"}},
|
||||
{Sha: "B", Parents: []string{"C"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⎔
|
||||
2 ⏣─╮
|
||||
3 ⏣─│─╮
|
||||
5 ⏣─│─│─╮
|
||||
7 ⏣─│─│─│─╮
|
||||
4 ⎔─┴─╯ │ │
|
||||
B ⎔ ╭───╯ │`,
|
||||
},
|
||||
{
|
||||
name: "with a path that has room to move to the left and continues",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2", "3"}},
|
||||
{Sha: "3", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"4", "5"}},
|
||||
{Sha: "4", Parents: []string{"6", "7"}},
|
||||
{Sha: "6", Parents: []string{"8"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⏣─╮
|
||||
3 │ ⎔
|
||||
2 ⏣─│
|
||||
4 ⏣─│─╮
|
||||
6 ⎔ │ │`,
|
||||
},
|
||||
{
|
||||
name: "new merge path fills gap before continuing path on right",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2", "3", "4", "5"}},
|
||||
{Sha: "4", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"A"}},
|
||||
{Sha: "A", Parents: []string{"6", "B"}},
|
||||
{Sha: "B", Parents: []string{"C"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⏣─┬─┬─╮
|
||||
4 │ │ ⎔ │
|
||||
2 ⎔─│─╯ │
|
||||
A ⏣─│─╮ │
|
||||
B │ │ ⎔ │`,
|
||||
},
|
||||
{
|
||||
name: "with a path that has room to move to the left and continues",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"3", "4"}},
|
||||
{Sha: "3", Parents: []string{"5", "4"}},
|
||||
{Sha: "5", Parents: []string{"7", "8"}},
|
||||
{Sha: "7", Parents: []string{"4", "A"}},
|
||||
{Sha: "4", Parents: []string{"B"}},
|
||||
{Sha: "B", Parents: []string{"C"}},
|
||||
{Sha: "C", Parents: []string{"D"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⎔
|
||||
2 ⏣─╮
|
||||
3 ⏣─│─╮
|
||||
5 ⏣─│─│─╮
|
||||
7 ⏣─│─│─│─╮
|
||||
4 ⎔─┴─╯ │ │
|
||||
B ⎔ ╭───╯ │
|
||||
C ⎔ │ ╭───╯`,
|
||||
},
|
||||
{
|
||||
name: "with a path that has room to move to the left and continues",
|
||||
commits: []*models.Commit{
|
||||
{Sha: "1", Parents: []string{"2"}},
|
||||
{Sha: "2", Parents: []string{"3", "4"}},
|
||||
{Sha: "3", Parents: []string{"5", "4"}},
|
||||
{Sha: "5", Parents: []string{"7", "G"}},
|
||||
{Sha: "7", Parents: []string{"8", "A"}},
|
||||
{Sha: "8", Parents: []string{"4", "E"}},
|
||||
{Sha: "4", Parents: []string{"B"}},
|
||||
{Sha: "B", Parents: []string{"C"}},
|
||||
{Sha: "C", Parents: []string{"D"}},
|
||||
{Sha: "D", Parents: []string{"F"}},
|
||||
},
|
||||
expectedOutput: `
|
||||
1 ⎔
|
||||
2 ⏣─╮
|
||||
3 ⏣─│─╮
|
||||
5 ⏣─│─│─╮
|
||||
7 ⏣─│─│─│─╮
|
||||
8 ⏣─│─│─│─│─╮
|
||||
4 ⎔─┴─╯ │ │ │
|
||||
B ⎔ ╭───╯ │ │
|
||||
C ⎔ │ ╭───╯ │
|
||||
D ⎔ │ │ ╭───╯`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
getStyle := func(c *models.Commit) style.TextStyle { return style.FgDefault }
|
||||
lines := RenderCommitGraph(test.commits, "blah", getStyle)
|
||||
|
||||
trimmedExpectedOutput := ""
|
||||
for _, line := range strings.Split(strings.TrimPrefix(test.expectedOutput, "\n"), "\n") {
|
||||
trimmedExpectedOutput += strings.TrimSpace(line) + "\n"
|
||||
}
|
||||
|
||||
t.Log("\nexpected: \n" + trimmedExpectedOutput)
|
||||
|
||||
output := ""
|
||||
for i, line := range lines {
|
||||
description := test.commits[i].Sha
|
||||
output += strings.TrimSpace(description+" "+utils.Decolorise(line)) + "\n"
|
||||
}
|
||||
t.Log("\nactual: \n" + output)
|
||||
|
||||
assert.Equal(t,
|
||||
trimmedExpectedOutput,
|
||||
output)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderPipeSet(t *testing.T) {
|
||||
cyan := style.FgCyan
|
||||
red := style.FgRed
|
||||
green := style.FgGreen
|
||||
// blue := style.FgBlue
|
||||
yellow := style.FgYellow
|
||||
magenta := style.FgMagenta
|
||||
nothing := style.Nothing
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pipes []Pipe
|
||||
commit *models.Commit
|
||||
prevCommit *models.Commit
|
||||
expectedStr string
|
||||
expectedStyles []style.TextStyle
|
||||
}{
|
||||
{
|
||||
name: "single cell",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: TERMINATES, style: cyan},
|
||||
{fromPos: 0, toPos: 0, fromSha: "b", toSha: "c", kind: STARTS, style: green},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a"},
|
||||
expectedStr: "⎔",
|
||||
expectedStyles: []style.TextStyle{green},
|
||||
},
|
||||
{
|
||||
name: "single cell, selected",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a", toSha: "selected", kind: TERMINATES, style: cyan},
|
||||
{fromPos: 0, toPos: 0, fromSha: "selected", toSha: "c", kind: STARTS, style: green},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a"},
|
||||
expectedStr: "⎔",
|
||||
expectedStyles: []style.TextStyle{highlightStyle},
|
||||
},
|
||||
{
|
||||
name: "terminating hook and starting hook, selected",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a", toSha: "selected", kind: TERMINATES, style: cyan},
|
||||
{fromPos: 1, toPos: 0, fromSha: "c", toSha: "selected", kind: TERMINATES, style: yellow},
|
||||
{fromPos: 0, toPos: 0, fromSha: "selected", toSha: "d", kind: STARTS, style: green},
|
||||
{fromPos: 0, toPos: 1, fromSha: "selected", toSha: "e", kind: STARTS, style: green},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a"},
|
||||
expectedStr: "⏣─╮",
|
||||
expectedStyles: []style.TextStyle{
|
||||
highlightStyle, highlightStyle, highlightStyle,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "terminating hook and starting hook, prioritise the starting one",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: TERMINATES, style: red},
|
||||
{fromPos: 1, toPos: 0, fromSha: "c", toSha: "b", kind: TERMINATES, style: magenta},
|
||||
{fromPos: 0, toPos: 0, fromSha: "b", toSha: "d", kind: STARTS, style: green},
|
||||
{fromPos: 0, toPos: 1, fromSha: "b", toSha: "e", kind: STARTS, style: green},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a"},
|
||||
expectedStr: "⏣─│",
|
||||
expectedStyles: []style.TextStyle{
|
||||
green, green, magenta,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "starting and terminating pipe sharing some space",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow},
|
||||
{fromPos: 1, toPos: 1, fromSha: "b1", toSha: "b2", kind: CONTINUES, style: magenta},
|
||||
{fromPos: 3, toPos: 0, fromSha: "e1", toSha: "a2", kind: TERMINATES, style: green},
|
||||
{fromPos: 0, toPos: 2, fromSha: "a2", toSha: "c3", kind: STARTS, style: yellow},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a1"},
|
||||
expectedStr: "⏣─│─┬─╯",
|
||||
expectedStyles: []style.TextStyle{
|
||||
yellow, yellow, magenta, yellow, yellow, green, green,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "starting and terminating pipe sharing some space, with selection",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a1", toSha: "selected", kind: TERMINATES, style: red},
|
||||
{fromPos: 0, toPos: 0, fromSha: "selected", toSha: "a3", kind: STARTS, style: yellow},
|
||||
{fromPos: 1, toPos: 1, fromSha: "b1", toSha: "b2", kind: CONTINUES, style: magenta},
|
||||
{fromPos: 3, toPos: 0, fromSha: "e1", toSha: "selected", kind: TERMINATES, style: green},
|
||||
{fromPos: 0, toPos: 2, fromSha: "selected", toSha: "c3", kind: STARTS, style: yellow},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a1"},
|
||||
expectedStr: "⏣───╮ ╯",
|
||||
expectedStyles: []style.TextStyle{
|
||||
highlightStyle, highlightStyle, highlightStyle, highlightStyle, highlightStyle, nothing, green,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "many terminating pipes",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow},
|
||||
{fromPos: 1, toPos: 0, fromSha: "b1", toSha: "a2", kind: TERMINATES, style: magenta},
|
||||
{fromPos: 2, toPos: 0, fromSha: "c1", toSha: "a2", kind: TERMINATES, style: green},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a1"},
|
||||
expectedStr: "⎔─┴─╯",
|
||||
expectedStyles: []style.TextStyle{
|
||||
yellow, magenta, magenta, green, green,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "starting pipe passing through",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow},
|
||||
{fromPos: 0, toPos: 3, fromSha: "a2", toSha: "d3", kind: STARTS, style: yellow},
|
||||
{fromPos: 1, toPos: 1, fromSha: "b1", toSha: "b3", kind: CONTINUES, style: magenta},
|
||||
{fromPos: 2, toPos: 2, fromSha: "c1", toSha: "c3", kind: CONTINUES, style: green},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a1"},
|
||||
expectedStr: "⏣─│─│─╮",
|
||||
expectedStyles: []style.TextStyle{
|
||||
yellow, yellow, magenta, yellow, green, yellow, yellow,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "starting and terminating path crossing continuing path",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow},
|
||||
{fromPos: 0, toPos: 1, fromSha: "a2", toSha: "b3", kind: STARTS, style: yellow},
|
||||
{fromPos: 1, toPos: 1, fromSha: "b1", toSha: "a2", kind: CONTINUES, style: green},
|
||||
{fromPos: 2, toPos: 0, fromSha: "c1", toSha: "a2", kind: TERMINATES, style: magenta},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a1"},
|
||||
expectedStr: "⏣─│─╯",
|
||||
expectedStyles: []style.TextStyle{
|
||||
yellow, yellow, green, magenta, magenta,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "another clash of starting and terminating paths",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow},
|
||||
{fromPos: 0, toPos: 1, fromSha: "a2", toSha: "b3", kind: STARTS, style: yellow},
|
||||
{fromPos: 2, toPos: 2, fromSha: "c1", toSha: "c3", kind: CONTINUES, style: green},
|
||||
{fromPos: 3, toPos: 0, fromSha: "d1", toSha: "a2", kind: TERMINATES, style: magenta},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "a1"},
|
||||
expectedStr: "⏣─┬─│─╯",
|
||||
expectedStyles: []style.TextStyle{
|
||||
yellow, yellow, yellow, magenta, green, magenta, magenta,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "commit whose previous commit is selected",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "selected", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: yellow},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "selected"},
|
||||
expectedStr: "⎔",
|
||||
expectedStyles: []style.TextStyle{
|
||||
yellow,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "commit whose previous commit is selected and is a merge commit",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "selected", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 1, toPos: 1, fromSha: "selected", toSha: "b3", kind: CONTINUES, style: red},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "selected"},
|
||||
expectedStr: "⎔ │",
|
||||
expectedStyles: []style.TextStyle{
|
||||
highlightStyle, nothing, highlightStyle,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "commit whose previous commit is selected and is a merge commit, with continuing pipe inbetween",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "selected", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 1, toPos: 1, fromSha: "z1", toSha: "z3", kind: CONTINUES, style: green},
|
||||
{fromPos: 2, toPos: 2, fromSha: "selected", toSha: "b3", kind: CONTINUES, style: red},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "selected"},
|
||||
expectedStr: "⎔ │ │",
|
||||
expectedStyles: []style.TextStyle{
|
||||
highlightStyle, nothing, green, nothing, highlightStyle,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "when previous commit is selected, not a merge commit, and spawns a continuing pipe",
|
||||
pipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a1", toSha: "a2", kind: TERMINATES, style: red},
|
||||
{fromPos: 0, toPos: 0, fromSha: "a2", toSha: "a3", kind: STARTS, style: green},
|
||||
{fromPos: 0, toPos: 1, fromSha: "a2", toSha: "b3", kind: STARTS, style: green},
|
||||
{fromPos: 1, toPos: 0, fromSha: "selected", toSha: "a2", kind: TERMINATES, style: yellow},
|
||||
},
|
||||
prevCommit: &models.Commit{Sha: "selected"},
|
||||
expectedStr: "⏣─╯",
|
||||
expectedStyles: []style.TextStyle{
|
||||
highlightStyle, highlightStyle, highlightStyle,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actualStr := renderPipeSet(test.pipes, "selected", test.prevCommit)
|
||||
t.Log("actual cells:")
|
||||
t.Log(actualStr)
|
||||
expectedStr := ""
|
||||
if len([]rune(test.expectedStr)) != len(test.expectedStyles) {
|
||||
t.Fatalf("Error in test setup: you have %d characters in the expected output (%s) but have specified %d styles", len([]rune(test.expectedStr)), test.expectedStr, len(test.expectedStyles))
|
||||
}
|
||||
for i, char := range []rune(test.expectedStr) {
|
||||
expectedStr += test.expectedStyles[i].Sprint(string(char))
|
||||
}
|
||||
expectedStr += " "
|
||||
t.Log("expected cells:")
|
||||
t.Log(expectedStr)
|
||||
|
||||
assert.Equal(t, expectedStr, actualStr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCellRender(t *testing.T) {
|
||||
tests := []struct {
|
||||
cell *Cell
|
||||
expectedString string
|
||||
}{
|
||||
{
|
||||
cell: &Cell{
|
||||
up: true,
|
||||
down: true,
|
||||
cellType: CONNECTION,
|
||||
style: style.FgCyan,
|
||||
},
|
||||
expectedString: "\x1b[36m│\x1b[0m ",
|
||||
},
|
||||
{
|
||||
cell: &Cell{
|
||||
up: true,
|
||||
down: true,
|
||||
cellType: COMMIT,
|
||||
style: style.FgCyan,
|
||||
},
|
||||
expectedString: "\x1b[36m⎔\x1b[0m ",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
assert.EqualValues(t, test.expectedString, test.cell.render())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNextPipes(t *testing.T) {
|
||||
tests := []struct {
|
||||
prevPipes []Pipe
|
||||
commit *models.Commit
|
||||
expected []Pipe
|
||||
}{
|
||||
{
|
||||
prevPipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: STARTS, style: style.FgDefault},
|
||||
},
|
||||
commit: &models.Commit{
|
||||
Sha: "b",
|
||||
Parents: []string{"c"},
|
||||
},
|
||||
expected: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: TERMINATES, style: style.FgDefault},
|
||||
{fromPos: 0, toPos: 0, fromSha: "b", toSha: "c", kind: STARTS, style: style.FgDefault},
|
||||
},
|
||||
},
|
||||
{
|
||||
prevPipes: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "a", toSha: "b", kind: TERMINATES, style: style.FgDefault},
|
||||
{fromPos: 0, toPos: 0, fromSha: "b", toSha: "c", kind: STARTS, style: style.FgDefault},
|
||||
{fromPos: 0, toPos: 1, fromSha: "b", toSha: "d", kind: STARTS, style: style.FgDefault},
|
||||
},
|
||||
commit: &models.Commit{
|
||||
Sha: "d",
|
||||
Parents: []string{"e"},
|
||||
},
|
||||
expected: []Pipe{
|
||||
{fromPos: 0, toPos: 0, fromSha: "b", toSha: "c", kind: CONTINUES, style: style.FgDefault},
|
||||
{fromPos: 1, toPos: 1, fromSha: "b", toSha: "d", kind: TERMINATES, style: style.FgDefault},
|
||||
{fromPos: 1, toPos: 1, fromSha: "d", toSha: "e", kind: STARTS, style: style.FgDefault},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
getStyle := func(c *models.Commit) style.TextStyle { return style.FgDefault }
|
||||
pipes := getNextPipes(test.prevPipes, test.commit, getStyle)
|
||||
// rendering cells so that it's easier to see what went wrong
|
||||
actualStr := renderPipeSet(pipes, "selected", nil)
|
||||
expectedStr := renderPipeSet(test.expected, "selected", nil)
|
||||
t.Log("expected cells:")
|
||||
t.Log(expectedStr)
|
||||
t.Log("actual cells:")
|
||||
t.Log(actualStr)
|
||||
assert.EqualValues(t, test.expected, pipes)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,19 @@ import (
|
|||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
)
|
||||
|
||||
type RebaseOption string
|
||||
|
||||
const (
|
||||
REBASE_OPTION_CONTINUE = "continue"
|
||||
REBASE_OPTION_ABORT = "abort"
|
||||
REBASE_OPTION_SKIP = "skip"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCreateRebaseOptionsMenu() error {
|
||||
options := []string{"continue", "abort"}
|
||||
options := []string{REBASE_OPTION_CONTINUE, REBASE_OPTION_ABORT}
|
||||
|
||||
if gui.GitCommand.WorkingTreeState() == commands.REBASE_MODE_REBASING {
|
||||
options = append(options, "skip")
|
||||
options = append(options, REBASE_OPTION_SKIP)
|
||||
}
|
||||
|
||||
menuItems := make([]*menuItem, len(options))
|
||||
|
|
@ -49,7 +57,7 @@ func (gui *Gui) genericMergeCommand(command string) error {
|
|||
// we should end up with a command like 'git merge --continue'
|
||||
|
||||
// it's impossible for a rebase to require a commit so we'll use a subprocess only if it's a merge
|
||||
if status == commands.REBASE_MODE_MERGING && command != "abort" && gui.Config.GetUserConfig().Git.Merging.ManualCommit {
|
||||
if status == commands.REBASE_MODE_MERGING && command != REBASE_OPTION_ABORT && gui.Config.GetUserConfig().Git.Merging.ManualCommit {
|
||||
sub := gitCommand.OSCommand.PrepareSubProcess("git", commandType, fmt.Sprintf("--%s", command))
|
||||
if sub != nil {
|
||||
return gui.runSubprocessWithSuspenseAndRefresh(sub)
|
||||
|
|
@ -87,9 +95,9 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
|||
if result == nil {
|
||||
return nil
|
||||
} else if strings.Contains(result.Error(), "No changes - did you forget to use") {
|
||||
return gui.genericMergeCommand("skip")
|
||||
return gui.genericMergeCommand(REBASE_OPTION_SKIP)
|
||||
} else if strings.Contains(result.Error(), "The previous cherry-pick is now empty") {
|
||||
return gui.genericMergeCommand("continue")
|
||||
return gui.genericMergeCommand(REBASE_OPTION_CONTINUE)
|
||||
} else if strings.Contains(result.Error(), "No rebase in progress?") {
|
||||
// assume in this case that we're already done
|
||||
return nil
|
||||
|
|
@ -106,10 +114,34 @@ func (gui *Gui) handleGenericMergeCommandResult(result error) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return gui.genericMergeCommand("abort")
|
||||
return gui.genericMergeCommand(REBASE_OPTION_ABORT)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
return gui.createErrorPanel(result.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) abortMergeOrRebaseWithConfirm() error {
|
||||
// prompt user to confirm that they want to abort, then do it
|
||||
mode := gui.workingTreeStateNoun()
|
||||
return gui.ask(askOpts{
|
||||
title: fmt.Sprintf(gui.Tr.AbortTitle, mode),
|
||||
prompt: fmt.Sprintf(gui.Tr.AbortPrompt, mode),
|
||||
handleConfirm: func() error {
|
||||
return gui.genericMergeCommand(REBASE_OPTION_ABORT)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) workingTreeStateNoun() string {
|
||||
workingTreeState := gui.GitCommand.WorkingTreeState()
|
||||
switch workingTreeState {
|
||||
case commands.REBASE_MODE_NORMAL:
|
||||
return ""
|
||||
case commands.REBASE_MODE_MERGING:
|
||||
return "merge"
|
||||
default:
|
||||
return "rebase"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
package style
|
||||
|
||||
import (
|
||||
"github.com/gookit/color"
|
||||
"text/template"
|
||||
|
||||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -16,6 +17,7 @@ var (
|
|||
FgBlue = FromBasicFg(color.FgBlue)
|
||||
FgYellow = FromBasicFg(color.FgYellow)
|
||||
FgMagenta = FromBasicFg(color.FgMagenta)
|
||||
FgDefault = FromBasicFg(color.FgDefault)
|
||||
|
||||
BgWhite = FromBasicBg(color.BgWhite)
|
||||
BgBlack = FromBasicBg(color.BgBlack)
|
||||
|
|
@ -26,6 +28,9 @@ var (
|
|||
BgMagenta = FromBasicBg(color.BgMagenta)
|
||||
BgCyan = FromBasicBg(color.BgCyan)
|
||||
|
||||
// will not print any colour escape codes, including the reset escape code
|
||||
Nothing = New()
|
||||
|
||||
AttrUnderline = New().SetUnderline()
|
||||
AttrBold = New().SetBold()
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@ import (
|
|||
"github.com/xo/terminfo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// on CI we've got no color capability so we're forcing it here
|
||||
color.ForceSetColorLevel(terminfo.ColorLevelMillions)
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
type scenario struct {
|
||||
name string
|
||||
|
|
@ -18,9 +23,6 @@ func TestMerge(t *testing.T) {
|
|||
expectedStr string
|
||||
}
|
||||
|
||||
// on CI we've got no color capability so we're forcing it here
|
||||
color.ForceSetColorLevel(terminfo.ColorLevelMillions)
|
||||
|
||||
fgRed := color.FgRed
|
||||
bgRed := color.BgRed
|
||||
fgBlue := color.FgBlue
|
||||
|
|
|
|||
|
|
@ -90,9 +90,8 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager {
|
|||
view.Reset()
|
||||
},
|
||||
func() {
|
||||
gui.g.Update(func(*gocui.Gui) error {
|
||||
return nil
|
||||
})
|
||||
// gui.g.Draw(view) // doing this causes an issue when there's a popup panel in front of the main view.
|
||||
gui.render()
|
||||
},
|
||||
func() {
|
||||
// Need to check if the content of the view is well past the origin.
|
||||
|
|
|
|||
|
|
@ -202,8 +202,7 @@ func (gui *Gui) cleanString(s string) string {
|
|||
}
|
||||
|
||||
func (gui *Gui) setViewContentSync(v *gocui.View, s string) {
|
||||
v.Clear()
|
||||
fmt.Fprint(v, gui.cleanString(s))
|
||||
v.SetContent(gui.cleanString(s))
|
||||
}
|
||||
|
||||
func (gui *Gui) setViewContent(v *gocui.View, s string) {
|
||||
|
|
@ -305,12 +304,8 @@ func (gui *Gui) refreshSelectedLine(panelState IListPanelState, total int) {
|
|||
}
|
||||
|
||||
func (gui *Gui) renderDisplayStrings(v *gocui.View, displayStrings [][]string) {
|
||||
gui.g.Update(func(g *gocui.Gui) error {
|
||||
list := utils.RenderDisplayStrings(displayStrings)
|
||||
v.Clear()
|
||||
fmt.Fprint(v, list)
|
||||
return nil
|
||||
})
|
||||
list := utils.RenderDisplayStrings(displayStrings)
|
||||
v.SetContent(list)
|
||||
}
|
||||
|
||||
func (gui *Gui) globalOptionsMap() map[string]string {
|
||||
|
|
@ -388,3 +383,7 @@ func getTabbedView(gui *Gui) *gocui.View {
|
|||
view, _ := gui.g.View(context.GetViewName())
|
||||
return view
|
||||
}
|
||||
|
||||
func (gui *Gui) render() {
|
||||
gui.g.Update(func(g *gocui.Gui) error { return nil })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -437,6 +437,8 @@ type TranslationSet struct {
|
|||
GitOutput string
|
||||
GitCommandFailed string
|
||||
OpenPr string
|
||||
AbortTitle string
|
||||
AbortPrompt string
|
||||
Spans Spans
|
||||
}
|
||||
|
||||
|
|
@ -967,7 +969,8 @@ func englishTranslationSet() TranslationSet {
|
|||
GitOutput: "Git output:",
|
||||
GitCommandFailed: "Git command failed. Check command log for details (open with %s)",
|
||||
OpenPr: "Open PR #",
|
||||
|
||||
AbortTitle: "Abort %s",
|
||||
AbortPrompt: "Are you sure you want to abort the current %s?",
|
||||
Spans: Spans{
|
||||
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
|
||||
CheckoutCommit: "Checkout commit",
|
||||
|
|
|
|||
|
|
@ -131,7 +131,6 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, prefix string
|
|||
|
||||
select {
|
||||
case <-stop:
|
||||
m.refreshView()
|
||||
break outer
|
||||
default:
|
||||
}
|
||||
|
|
@ -139,18 +138,18 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, prefix string
|
|||
// if we're here then there's nothing left to scan from the source
|
||||
// so we're at the EOF and can flush the stale content
|
||||
m.onEndOfInput()
|
||||
m.refreshView()
|
||||
break outer
|
||||
}
|
||||
_, _ = m.writer.Write(append(scanner.Bytes(), '\n'))
|
||||
}
|
||||
m.refreshView()
|
||||
case <-stop:
|
||||
m.refreshView()
|
||||
break outer
|
||||
}
|
||||
}
|
||||
|
||||
m.refreshView()
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
// it's fine if we've killed this program ourselves
|
||||
if !strings.Contains(err.Error(), "signal: killed") {
|
||||
|
|
@ -158,8 +157,6 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, prefix string
|
|||
}
|
||||
}
|
||||
|
||||
m.refreshView()
|
||||
|
||||
if onDone != nil {
|
||||
onDone()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
|
|
@ -66,7 +67,7 @@ func main() {
|
|||
app := &App{testDir: testDir}
|
||||
app.loadTests()
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, false)
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, false, gui.RuneReplacements)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
|
|
|||
172
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
172
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
|
|
@ -6,7 +6,6 @@ package gocui
|
|||
|
||||
import (
|
||||
standardErrors "errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
|
@ -76,6 +75,8 @@ type GuiMutexes struct {
|
|||
tickingMutex sync.Mutex
|
||||
|
||||
ViewsMutex sync.Mutex
|
||||
|
||||
drawMutex sync.Mutex
|
||||
}
|
||||
|
||||
type PlayMode int
|
||||
|
|
@ -166,14 +167,14 @@ type Gui struct {
|
|||
}
|
||||
|
||||
// NewGui returns a new Gui object with a given output mode.
|
||||
func NewGui(mode OutputMode, supportOverlaps bool, playMode PlayMode, headless bool) (*Gui, error) {
|
||||
func NewGui(mode OutputMode, supportOverlaps bool, playMode PlayMode, headless bool, runeReplacements map[rune]string) (*Gui, error) {
|
||||
g := &Gui{}
|
||||
|
||||
var err error
|
||||
if headless {
|
||||
err = g.tcellInitSimulation()
|
||||
} else {
|
||||
err = g.tcellInit()
|
||||
err = g.tcellInit(runeReplacements)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -664,79 +665,6 @@ func (g *Gui) onResize() {
|
|||
// g.screen.Sync()
|
||||
}
|
||||
|
||||
// flush updates the gui, re-drawing frames and buffers.
|
||||
func (g *Gui) flush() error {
|
||||
// pretty sure we don't need this, but keeping it here in case we get weird visual artifacts
|
||||
// g.clear(g.FgColor, g.BgColor)
|
||||
|
||||
maxX, maxY := Screen.Size()
|
||||
// if GUI's size has changed, we need to redraw all views
|
||||
if maxX != g.maxX || maxY != g.maxY {
|
||||
for _, v := range g.views {
|
||||
v.clearViewLines()
|
||||
}
|
||||
}
|
||||
g.maxX, g.maxY = maxX, maxY
|
||||
|
||||
for _, m := range g.managers {
|
||||
if err := m.Layout(g); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, v := range g.views {
|
||||
if !v.Visible || v.y1 < v.y0 {
|
||||
continue
|
||||
}
|
||||
if v.Frame {
|
||||
var fgColor, bgColor, frameColor Attribute
|
||||
if g.Highlight && v == g.currentView {
|
||||
fgColor = g.SelFgColor
|
||||
bgColor = g.SelBgColor
|
||||
frameColor = g.SelFrameColor
|
||||
} else {
|
||||
bgColor = g.BgColor
|
||||
if v.TitleColor != ColorDefault {
|
||||
fgColor = v.TitleColor
|
||||
} else {
|
||||
fgColor = g.FgColor
|
||||
}
|
||||
if v.FrameColor != ColorDefault {
|
||||
frameColor = v.FrameColor
|
||||
} else {
|
||||
frameColor = g.FrameColor
|
||||
}
|
||||
}
|
||||
|
||||
if err := g.drawFrameEdges(v, frameColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.drawFrameCorners(v, frameColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
if v.Title != "" || len(v.Tabs) > 0 {
|
||||
if err := g.drawTitle(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v.Subtitle != "" {
|
||||
if err := g.drawSubtitle(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v.ContainsList && g.ShowListFooter {
|
||||
if err := g.drawListFooter(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := g.draw(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
Screen.Show()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Gui) clear(fg, bg Attribute) (int, int) {
|
||||
st := getTcellStyle(oldStyle{fg: fg, bg: bg, outputMode: g.outputMode})
|
||||
w, h := Screen.Size()
|
||||
|
|
@ -983,7 +911,7 @@ func (g *Gui) drawListFooter(v *View, fgColor, bgColor Attribute) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("%d of %d", v.cy+v.oy+1, len(v.lines))
|
||||
message := v.Footer
|
||||
|
||||
if v.y1 < 0 || v.y1 >= g.maxY {
|
||||
return nil
|
||||
|
|
@ -1006,12 +934,102 @@ func (g *Gui) drawListFooter(v *View, fgColor, bgColor Attribute) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// flush updates the gui, re-drawing frames and buffers.
|
||||
func (g *Gui) flush() error {
|
||||
g.Mutexes.drawMutex.Lock()
|
||||
defer g.Mutexes.drawMutex.Unlock()
|
||||
|
||||
// pretty sure we don't need this, but keeping it here in case we get weird visual artifacts
|
||||
// g.clear(g.FgColor, g.BgColor)
|
||||
|
||||
maxX, maxY := Screen.Size()
|
||||
// if GUI's size has changed, we need to redraw all views
|
||||
if maxX != g.maxX || maxY != g.maxY {
|
||||
for _, v := range g.views {
|
||||
v.clearViewLines()
|
||||
}
|
||||
}
|
||||
g.maxX, g.maxY = maxX, maxY
|
||||
|
||||
for _, m := range g.managers {
|
||||
if err := m.Layout(g); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, v := range g.views {
|
||||
if err := g.draw(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
Screen.Show()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Gui) Draw(v *View) error {
|
||||
g.Mutexes.drawMutex.Lock()
|
||||
defer g.Mutexes.drawMutex.Unlock()
|
||||
|
||||
if err := g.draw(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Screen.Show()
|
||||
return nil
|
||||
}
|
||||
|
||||
// draw manages the cursor and calls the draw function of a view.
|
||||
func (g *Gui) draw(v *View) error {
|
||||
if g.suspended {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !v.Visible || v.y1 < v.y0 {
|
||||
return nil
|
||||
}
|
||||
if v.Frame {
|
||||
var fgColor, bgColor, frameColor Attribute
|
||||
if g.Highlight && v == g.currentView {
|
||||
fgColor = g.SelFgColor
|
||||
bgColor = g.SelBgColor
|
||||
frameColor = g.SelFrameColor
|
||||
} else {
|
||||
bgColor = g.BgColor
|
||||
if v.TitleColor != ColorDefault {
|
||||
fgColor = v.TitleColor
|
||||
} else {
|
||||
fgColor = g.FgColor
|
||||
}
|
||||
if v.FrameColor != ColorDefault {
|
||||
frameColor = v.FrameColor
|
||||
} else {
|
||||
frameColor = g.FrameColor
|
||||
}
|
||||
}
|
||||
|
||||
if err := g.drawFrameEdges(v, frameColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.drawFrameCorners(v, frameColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
if v.Title != "" || len(v.Tabs) > 0 {
|
||||
if err := g.drawTitle(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v.Subtitle != "" {
|
||||
if err := g.drawSubtitle(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v.Footer != "" && g.ShowListFooter {
|
||||
if err := g.drawListFooter(v, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if g.Cursor {
|
||||
if curview := g.currentView; curview != nil {
|
||||
vMaxX, vMaxY := curview.Size()
|
||||
|
|
|
|||
17
vendor/github.com/jesseduffield/gocui/tcell_driver.go
generated
vendored
17
vendor/github.com/jesseduffield/gocui/tcell_driver.go
generated
vendored
|
|
@ -26,6 +26,10 @@ var runeReplacements = map[rune]string{
|
|||
'┐': "+",
|
||||
'└': "+",
|
||||
'┘': "+",
|
||||
'╭': "+",
|
||||
'╮': "+",
|
||||
'╰': "+",
|
||||
'╯': "+",
|
||||
'─': "-",
|
||||
|
||||
// using a hyphen here actually looks weird.
|
||||
|
|
@ -33,6 +37,9 @@ var runeReplacements = map[rune]string{
|
|||
'╶': " ",
|
||||
'╴': " ",
|
||||
|
||||
'┴': "+",
|
||||
'┬': "+",
|
||||
'╷': "|",
|
||||
'├': "+",
|
||||
'│': "|",
|
||||
'▼': "v",
|
||||
|
|
@ -42,7 +49,7 @@ var runeReplacements = map[rune]string{
|
|||
}
|
||||
|
||||
// tcellInit initializes tcell screen for use.
|
||||
func (g *Gui) tcellInit() error {
|
||||
func (g *Gui) tcellInit(runeReplacements map[rune]string) error {
|
||||
runewidth.DefaultCondition.EastAsianWidth = false
|
||||
tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)
|
||||
|
||||
|
|
@ -51,7 +58,7 @@ func (g *Gui) tcellInit() error {
|
|||
} else if e = s.Init(); e != nil {
|
||||
return e
|
||||
} else {
|
||||
registerRuneFallbacks(s)
|
||||
registerRuneFallbacks(s, runeReplacements)
|
||||
|
||||
g.screen = s
|
||||
Screen = s
|
||||
|
|
@ -59,10 +66,14 @@ func (g *Gui) tcellInit() error {
|
|||
}
|
||||
}
|
||||
|
||||
func registerRuneFallbacks(s tcell.Screen) {
|
||||
func registerRuneFallbacks(s tcell.Screen, additional map[rune]string) {
|
||||
for before, after := range runeReplacements {
|
||||
s.RegisterRuneFallback(before, after)
|
||||
}
|
||||
|
||||
for before, after := range additional {
|
||||
s.RegisterRuneFallback(before, after)
|
||||
}
|
||||
}
|
||||
|
||||
// tcellInitSimulation initializes tcell screen for use.
|
||||
|
|
|
|||
69
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
69
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
|
|
@ -153,14 +153,14 @@ type View struct {
|
|||
|
||||
searcher *searcher
|
||||
|
||||
// when ContainsList is true, we show the current index and total count in the view
|
||||
ContainsList bool
|
||||
|
||||
// KeybindOnEdit should be set to true when you want to execute keybindings even when the view is editable
|
||||
// (this is usually not the case)
|
||||
KeybindOnEdit bool
|
||||
|
||||
TextArea *TextArea
|
||||
|
||||
// something like '1 of 20' for a list view
|
||||
Footer string
|
||||
}
|
||||
|
||||
// call this in the event of a view resize, or if you want to render new content
|
||||
|
|
@ -372,11 +372,21 @@ func (v *View) Height() int {
|
|||
|
||||
// if a view has a frame, that leaves less space for its writeable area
|
||||
func (v *View) InnerWidth() int {
|
||||
return v.Width() - v.frameOffset()
|
||||
innerWidth := v.Width() - v.frameOffset()
|
||||
if innerWidth < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return innerWidth
|
||||
}
|
||||
|
||||
func (v *View) InnerHeight() int {
|
||||
return v.Height() - v.frameOffset()
|
||||
innerHeight := v.Height() - v.frameOffset()
|
||||
if innerHeight < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return innerHeight
|
||||
}
|
||||
|
||||
func (v *View) frameOffset() int {
|
||||
|
|
@ -563,8 +573,6 @@ func (v *View) Write(p []byte) (n int, err error) {
|
|||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
v.tainted = true
|
||||
v.makeWriteable(v.wx, v.wy)
|
||||
v.writeRunes(bytes.Runes(p))
|
||||
|
||||
return len(p), nil
|
||||
|
|
@ -574,20 +582,16 @@ func (v *View) WriteRunes(p []rune) {
|
|||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
v.writeRunes(p)
|
||||
}
|
||||
|
||||
// writeRunes copies slice of runes into internal lines buffer.
|
||||
func (v *View) writeRunes(p []rune) {
|
||||
v.tainted = true
|
||||
|
||||
// Fill with empty cells, if writing outside current view buffer
|
||||
v.makeWriteable(v.wx, v.wy)
|
||||
v.writeRunes(p)
|
||||
}
|
||||
|
||||
func (v *View) WriteString(s string) {
|
||||
v.WriteRunes([]rune(s))
|
||||
}
|
||||
|
||||
// writeRunes copies slice of runes into internal lines buffer.
|
||||
// caller must make sure that writing position is accessable.
|
||||
func (v *View) writeRunes(p []rune) {
|
||||
for _, r := range p {
|
||||
switch r {
|
||||
case '\n':
|
||||
|
|
@ -613,6 +617,16 @@ func (v *View) writeRunes(p []rune) {
|
|||
}
|
||||
}
|
||||
|
||||
// exported functions use the mutex. Non-exported functions are for internal use
|
||||
// and a calling function should use a mutex
|
||||
func (v *View) WriteString(s string) {
|
||||
v.WriteRunes([]rune(s))
|
||||
}
|
||||
|
||||
func (v *View) writeString(s string) {
|
||||
v.writeRunes([]rune(s))
|
||||
}
|
||||
|
||||
// parseInput parses char by char the input written to the View. It returns nil
|
||||
// while processing ESC sequences. Otherwise, it returns a cell slice that
|
||||
// contains the processed data.
|
||||
|
|
@ -696,16 +710,28 @@ func (v *View) Read(p []byte) (n int, err error) {
|
|||
return offset, io.EOF
|
||||
}
|
||||
|
||||
// only use this if the calling function has a lock on writeMutex
|
||||
func (v *View) clear() {
|
||||
v.rewind()
|
||||
v.lines = nil
|
||||
v.clearViewLines()
|
||||
}
|
||||
|
||||
// Clear empties the view's internal buffer.
|
||||
// And resets reading and writing offsets.
|
||||
func (v *View) Clear() {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
v.rewind()
|
||||
v.lines = nil
|
||||
v.clearViewLines()
|
||||
v.clearRunes()
|
||||
v.clear()
|
||||
}
|
||||
|
||||
func (v *View) SetContent(str string) {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
v.clear()
|
||||
v.writeString(str)
|
||||
}
|
||||
|
||||
// Rewind sets read and write pos to (0, 0).
|
||||
|
|
@ -802,6 +828,9 @@ func (v *View) IsTainted() bool {
|
|||
|
||||
// draw re-draws the view's contents.
|
||||
func (v *View) draw() error {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
v.clearRunes()
|
||||
|
||||
if !v.Visible {
|
||||
|
|
|
|||
2
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
2
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
|
|
@ -1,6 +1,6 @@
|
|||
# go-colorable
|
||||
|
||||
[](https://travis-ci.org/mattn/go-colorable)
|
||||
[](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest)
|
||||
[](https://codecov.io/gh/mattn/go-colorable)
|
||||
[](http://godoc.org/github.com/mattn/go-colorable)
|
||||
[](https://goreportcard.com/report/mattn/go-colorable)
|
||||
|
|
|
|||
1
vendor/github.com/mattn/go-colorable/colorable_appengine.go
generated
vendored
1
vendor/github.com/mattn/go-colorable/colorable_appengine.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
|||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package colorable
|
||||
|
|
|
|||
4
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
4
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
// +build !windows
|
||||
// +build !appengine
|
||||
//go:build !windows && !appengine
|
||||
// +build !windows,!appengine
|
||||
|
||||
package colorable
|
||||
|
||||
|
|
|
|||
18
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
18
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
// +build windows
|
||||
// +build !appengine
|
||||
//go:build windows && !appengine
|
||||
// +build windows,!appengine
|
||||
|
||||
package colorable
|
||||
|
||||
|
|
@ -452,18 +452,22 @@ func (w *Writer) Write(data []byte) (n int, err error) {
|
|||
} else {
|
||||
er = bytes.NewReader(data)
|
||||
}
|
||||
var bw [1]byte
|
||||
var plaintext bytes.Buffer
|
||||
loop:
|
||||
for {
|
||||
c1, err := er.ReadByte()
|
||||
if err != nil {
|
||||
plaintext.WriteTo(w.out)
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
bw[0] = c1
|
||||
w.out.Write(bw[:])
|
||||
plaintext.WriteByte(c1)
|
||||
continue
|
||||
}
|
||||
_, err = plaintext.WriteTo(w.out)
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
c2, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
|
|
@ -719,7 +723,7 @@ loop:
|
|||
n256setup()
|
||||
}
|
||||
attr &= backgroundMask
|
||||
attr |= n256foreAttr[n256]
|
||||
attr |= n256foreAttr[n256%len(n256foreAttr)]
|
||||
i += 2
|
||||
}
|
||||
} else if len(token) == 5 && token[i+1] == "2" {
|
||||
|
|
@ -761,7 +765,7 @@ loop:
|
|||
n256setup()
|
||||
}
|
||||
attr &= foregroundMask
|
||||
attr |= n256backAttr[n256]
|
||||
attr |= n256backAttr[n256%len(n256backAttr)]
|
||||
i += 2
|
||||
}
|
||||
} else if len(token) == 5 && token[i+1] == "2" {
|
||||
|
|
|
|||
4
vendor/github.com/mattn/go-colorable/go.mod
generated
vendored
4
vendor/github.com/mattn/go-colorable/go.mod
generated
vendored
|
|
@ -1,8 +1,8 @@
|
|||
module github.com/mattn/go-colorable
|
||||
|
||||
require (
|
||||
github.com/mattn/go-isatty v0.0.12
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
|
||||
)
|
||||
|
||||
go 1.13
|
||||
|
|
|
|||
10
vendor/github.com/mattn/go-colorable/go.sum
generated
vendored
10
vendor/github.com/mattn/go-colorable/go.sum
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
|
|||
10
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
10
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
|
|
@ -18,18 +18,22 @@ func NewNonColorable(w io.Writer) io.Writer {
|
|||
// Write writes data on console
|
||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||
er := bytes.NewReader(data)
|
||||
var bw [1]byte
|
||||
var plaintext bytes.Buffer
|
||||
loop:
|
||||
for {
|
||||
c1, err := er.ReadByte()
|
||||
if err != nil {
|
||||
plaintext.WriteTo(w.out)
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
bw[0] = c1
|
||||
w.out.Write(bw[:])
|
||||
plaintext.WriteByte(c1)
|
||||
continue
|
||||
}
|
||||
_, err = plaintext.WriteTo(w.out)
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
c2, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
|
|
|
|||
2
vendor/github.com/mattn/go-isatty/go.mod
generated
vendored
2
vendor/github.com/mattn/go-isatty/go.mod
generated
vendored
|
|
@ -2,4 +2,4 @@ module github.com/mattn/go-isatty
|
|||
|
||||
go 1.12
|
||||
|
||||
require golang.org/x/sys v0.0.0-20200116001909-b77594299b42
|
||||
require golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
|
|
|
|||
1
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
1
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
|||
//go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
|
|
|
|||
3
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
3
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
|
|
@ -1,4 +1,5 @@
|
|||
// +build appengine js nacl
|
||||
//go:build appengine || js || nacl || wasm
|
||||
// +build appengine js nacl wasm
|
||||
|
||||
package isatty
|
||||
|
||||
|
|
|
|||
1
vendor/github.com/mattn/go-isatty/isatty_plan9.go
generated
vendored
1
vendor/github.com/mattn/go-isatty/isatty_plan9.go
generated
vendored
|
|
@ -1,3 +1,4 @@
|
|||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
package isatty
|
||||
|
|
|
|||
9
vendor/github.com/mattn/go-isatty/isatty_solaris.go
generated
vendored
9
vendor/github.com/mattn/go-isatty/isatty_solaris.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
// +build solaris
|
||||
// +build !appengine
|
||||
//go:build solaris && !appengine
|
||||
// +build solaris,!appengine
|
||||
|
||||
package isatty
|
||||
|
||||
|
|
@ -8,10 +8,9 @@ import (
|
|||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
|
||||
// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var termio unix.Termio
|
||||
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
|
||||
_, err := unix.IoctlGetTermio(int(fd), unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
|
|
|
|||
3
vendor/github.com/mattn/go-isatty/isatty_tcgets.go
generated
vendored
3
vendor/github.com/mattn/go-isatty/isatty_tcgets.go
generated
vendored
|
|
@ -1,4 +1,5 @@
|
|||
// +build linux aix
|
||||
//go:build (linux || aix || zos) && !appengine
|
||||
// +build linux aix zos
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
|
|
|||
6
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
6
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
|
|
@ -1,5 +1,5 @@
|
|||
// +build windows
|
||||
// +build !appengine
|
||||
//go:build windows && !appengine
|
||||
// +build windows,!appengine
|
||||
|
||||
package isatty
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ func isCygwinPipeName(name string) bool {
|
|||
}
|
||||
|
||||
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
|
||||
// since GetFileInformationByHandleEx is not avilable under windows Vista and still some old fashion
|
||||
// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion
|
||||
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
|
||||
// Windows vista to 10
|
||||
// see https://stackoverflow.com/a/18792477 for details
|
||||
|
|
|
|||
8
vendor/github.com/mattn/go-isatty/renovate.json
generated
vendored
8
vendor/github.com/mattn/go-isatty/renovate.json
generated
vendored
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"postUpdateOptions": [
|
||||
"gomodTidy"
|
||||
]
|
||||
}
|
||||
31
vendor/golang.org/x/sys/unix/sockcmsg_linux.go
generated
vendored
31
vendor/golang.org/x/sys/unix/sockcmsg_linux.go
generated
vendored
|
|
@ -56,3 +56,34 @@ func PktInfo6(info *Inet6Pktinfo) []byte {
|
|||
*(*Inet6Pktinfo)(h.data(0)) = *info
|
||||
return b
|
||||
}
|
||||
|
||||
// ParseOrigDstAddr decodes a socket control message containing the original
|
||||
// destination address. To receive such a message the IP_RECVORIGDSTADDR or
|
||||
// IPV6_RECVORIGDSTADDR option must be enabled on the socket.
|
||||
func ParseOrigDstAddr(m *SocketControlMessage) (Sockaddr, error) {
|
||||
switch {
|
||||
case m.Header.Level == SOL_IP && m.Header.Type == IP_ORIGDSTADDR:
|
||||
pp := (*RawSockaddrInet4)(unsafe.Pointer(&m.Data[0]))
|
||||
sa := new(SockaddrInet4)
|
||||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
return sa, nil
|
||||
|
||||
case m.Header.Level == SOL_IPV6 && m.Header.Type == IPV6_ORIGDSTADDR:
|
||||
pp := (*RawSockaddrInet6)(unsafe.Pointer(&m.Data[0]))
|
||||
sa := new(SockaddrInet6)
|
||||
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
|
||||
sa.Port = int(p[0])<<8 + int(p[1])
|
||||
sa.ZoneId = pp.Scope_id
|
||||
for i := 0; i < len(sa.Addr); i++ {
|
||||
sa.Addr[i] = pp.Addr[i]
|
||||
}
|
||||
return sa, nil
|
||||
|
||||
default:
|
||||
return nil, EINVAL
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go
generated
vendored
6
vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go
generated
vendored
|
|
@ -641,13 +641,13 @@ type Eproc struct {
|
|||
Tdev int32
|
||||
Tpgid int32
|
||||
Tsess uintptr
|
||||
Wmesg [8]int8
|
||||
Wmesg [8]byte
|
||||
Xsize int32
|
||||
Xrssize int16
|
||||
Xccount int16
|
||||
Xswrss int16
|
||||
Flag int32
|
||||
Login [12]int8
|
||||
Login [12]byte
|
||||
Spare [4]int32
|
||||
_ [4]byte
|
||||
}
|
||||
|
|
@ -688,7 +688,7 @@ type ExternProc struct {
|
|||
P_priority uint8
|
||||
P_usrpri uint8
|
||||
P_nice int8
|
||||
P_comm [17]int8
|
||||
P_comm [17]byte
|
||||
P_pgrp uintptr
|
||||
P_addr uintptr
|
||||
P_xstat uint16
|
||||
|
|
|
|||
6
vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go
generated
vendored
6
vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go
generated
vendored
|
|
@ -641,13 +641,13 @@ type Eproc struct {
|
|||
Tdev int32
|
||||
Tpgid int32
|
||||
Tsess uintptr
|
||||
Wmesg [8]int8
|
||||
Wmesg [8]byte
|
||||
Xsize int32
|
||||
Xrssize int16
|
||||
Xccount int16
|
||||
Xswrss int16
|
||||
Flag int32
|
||||
Login [12]int8
|
||||
Login [12]byte
|
||||
Spare [4]int32
|
||||
_ [4]byte
|
||||
}
|
||||
|
|
@ -688,7 +688,7 @@ type ExternProc struct {
|
|||
P_priority uint8
|
||||
P_usrpri uint8
|
||||
P_nice int8
|
||||
P_comm [17]int8
|
||||
P_comm [17]byte
|
||||
P_pgrp uintptr
|
||||
P_addr uintptr
|
||||
P_xstat uint16
|
||||
|
|
|
|||
12
vendor/golang.org/x/sys/windows/service.go
generated
vendored
12
vendor/golang.org/x/sys/windows/service.go
generated
vendored
|
|
@ -17,8 +17,6 @@ const (
|
|||
SC_MANAGER_ALL_ACCESS = 0xf003f
|
||||
)
|
||||
|
||||
//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenSCManagerW
|
||||
|
||||
const (
|
||||
SERVICE_KERNEL_DRIVER = 1
|
||||
SERVICE_FILE_SYSTEM_DRIVER = 2
|
||||
|
|
@ -133,6 +131,14 @@ const (
|
|||
SC_EVENT_DATABASE_CHANGE = 0
|
||||
SC_EVENT_PROPERTY_CHANGE = 1
|
||||
SC_EVENT_STATUS_CHANGE = 2
|
||||
|
||||
SERVICE_START_REASON_DEMAND = 0x00000001
|
||||
SERVICE_START_REASON_AUTO = 0x00000002
|
||||
SERVICE_START_REASON_TRIGGER = 0x00000004
|
||||
SERVICE_START_REASON_RESTART_ON_FAILURE = 0x00000008
|
||||
SERVICE_START_REASON_DELAYEDAUTO = 0x00000010
|
||||
|
||||
SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON = 1
|
||||
)
|
||||
|
||||
type SERVICE_STATUS struct {
|
||||
|
|
@ -217,6 +223,7 @@ type QUERY_SERVICE_LOCK_STATUS struct {
|
|||
LockDuration uint32
|
||||
}
|
||||
|
||||
//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenSCManagerW
|
||||
//sys CloseServiceHandle(handle Handle) (err error) = advapi32.CloseServiceHandle
|
||||
//sys CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access uint32, srvType uint32, startType uint32, errCtl uint32, pathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16) (handle Handle, err error) [failretval==0] = advapi32.CreateServiceW
|
||||
//sys OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenServiceW
|
||||
|
|
@ -237,3 +244,4 @@ type QUERY_SERVICE_LOCK_STATUS struct {
|
|||
//sys SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) = sechost.SubscribeServiceChangeNotifications?
|
||||
//sys UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications?
|
||||
//sys RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) = advapi32.RegisterServiceCtrlHandlerExW
|
||||
//sys QueryServiceDynamicInformation(service Handle, infoLevel uint32, dynamicInfo unsafe.Pointer) (err error) = advapi32.QueryServiceDynamicInformation?
|
||||
|
|
|
|||
31
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
31
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
|
|
@ -115,6 +115,7 @@ var (
|
|||
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||
procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W")
|
||||
procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW")
|
||||
procQueryServiceDynamicInformation = modadvapi32.NewProc("QueryServiceDynamicInformation")
|
||||
procQueryServiceLockStatusW = modadvapi32.NewProc("QueryServiceLockStatusW")
|
||||
procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
|
||||
procQueryServiceStatusEx = modadvapi32.NewProc("QueryServiceStatusEx")
|
||||
|
|
@ -366,9 +367,9 @@ var (
|
|||
procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
|
||||
procNtCreateFile = modntdll.NewProc("NtCreateFile")
|
||||
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
|
||||
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
|
||||
procNtQueryInformationProcess = modntdll.NewProc("NtQueryInformationProcess")
|
||||
procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation")
|
||||
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
|
||||
procNtSetInformationProcess = modntdll.NewProc("NtSetInformationProcess")
|
||||
procNtSetSystemInformation = modntdll.NewProc("NtSetSystemInformation")
|
||||
procRtlAddFunctionTable = modntdll.NewProc("RtlAddFunctionTable")
|
||||
|
|
@ -976,6 +977,18 @@ func QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, buf
|
|||
return
|
||||
}
|
||||
|
||||
func QueryServiceDynamicInformation(service Handle, infoLevel uint32, dynamicInfo unsafe.Pointer) (err error) {
|
||||
err = procQueryServiceDynamicInformation.Find()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall(procQueryServiceDynamicInformation.Addr(), 3, uintptr(service), uintptr(infoLevel), uintptr(dynamicInfo))
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, bufSize uint32, bytesNeeded *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procQueryServiceLockStatusW.Addr(), 4, uintptr(mgr), uintptr(unsafe.Pointer(lockStatus)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0, 0)
|
||||
if r1 == 0 {
|
||||
|
|
@ -3171,14 +3184,6 @@ func NtCreateNamedPipeFile(pipe *Handle, access uint32, oa *OBJECT_ATTRIBUTES, i
|
|||
return
|
||||
}
|
||||
|
||||
func NtSetInformationFile(handle Handle, iosb *IO_STATUS_BLOCK, inBuffer *byte, inBufferLen uint32, class uint32) (ntstatus error) {
|
||||
r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferLen), uintptr(class), 0)
|
||||
if r0 != 0 {
|
||||
ntstatus = NTStatus(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NtQueryInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.Pointer, procInfoLen uint32, retLen *uint32) (ntstatus error) {
|
||||
r0, _, _ := syscall.Syscall6(procNtQueryInformationProcess.Addr(), 5, uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), uintptr(unsafe.Pointer(retLen)), 0)
|
||||
if r0 != 0 {
|
||||
|
|
@ -3195,6 +3200,14 @@ func NtQuerySystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInf
|
|||
return
|
||||
}
|
||||
|
||||
func NtSetInformationFile(handle Handle, iosb *IO_STATUS_BLOCK, inBuffer *byte, inBufferLen uint32, class uint32) (ntstatus error) {
|
||||
r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferLen), uintptr(class), 0)
|
||||
if r0 != 0 {
|
||||
ntstatus = NTStatus(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func NtSetInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.Pointer, procInfoLen uint32) (ntstatus error) {
|
||||
r0, _, _ := syscall.Syscall6(procNtSetInformationProcess.Addr(), 4, uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), 0, 0)
|
||||
if r0 != 0 {
|
||||
|
|
|
|||
15
vendor/modules.txt
vendored
15
vendor/modules.txt
vendored
|
|
@ -17,7 +17,6 @@ github.com/cloudfoundry/jibber_jabber
|
|||
## explicit
|
||||
github.com/creack/pty
|
||||
# github.com/davecgh/go-spew v1.1.1
|
||||
## explicit
|
||||
github.com/davecgh/go-spew/spew
|
||||
# github.com/emirpasic/gods v1.12.0
|
||||
github.com/emirpasic/gods/containers
|
||||
|
|
@ -105,7 +104,7 @@ github.com/gobwas/glob/util/strings
|
|||
github.com/golang-collections/collections/stack
|
||||
# github.com/golang/protobuf v1.3.2
|
||||
## explicit
|
||||
# github.com/google/go-cmp v0.3.1
|
||||
# github.com/google/go-cmp v0.5.6
|
||||
## explicit
|
||||
# github.com/gookit/color v1.4.2
|
||||
## explicit
|
||||
|
|
@ -116,8 +115,6 @@ github.com/imdario/mergo
|
|||
# github.com/integrii/flaggy v1.4.0
|
||||
## explicit
|
||||
github.com/integrii/flaggy
|
||||
# github.com/iriri/minimal/gitignore v0.3.2
|
||||
## explicit
|
||||
# github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
|
||||
github.com/jbenet/go-context/io
|
||||
# github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
||||
|
|
@ -164,7 +161,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
|
|||
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75
|
||||
## explicit
|
||||
github.com/jesseduffield/gocui
|
||||
# github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
|
|
@ -191,10 +188,10 @@ github.com/kyokomi/emoji/v2
|
|||
# github.com/lucasb-eyer/go-colorful v1.2.0
|
||||
## explicit
|
||||
github.com/lucasb-eyer/go-colorful
|
||||
# github.com/mattn/go-colorable v0.1.7
|
||||
# github.com/mattn/go-colorable v0.1.11
|
||||
## explicit
|
||||
github.com/mattn/go-colorable
|
||||
# github.com/mattn/go-isatty v0.0.12
|
||||
# github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mattn/go-isatty
|
||||
# github.com/mattn/go-runewidth v0.0.13
|
||||
## explicit
|
||||
|
|
@ -208,8 +205,6 @@ github.com/mitchellh/go-homedir
|
|||
## explicit
|
||||
# github.com/onsi/gomega v1.7.1
|
||||
## explicit
|
||||
# github.com/ozeidan/fuzzy-patricia v3.0.0+incompatible
|
||||
## explicit
|
||||
# github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/pmezard/go-difflib/difflib
|
||||
# github.com/rivo/uniseg v0.2.0
|
||||
|
|
@ -258,7 +253,7 @@ golang.org/x/crypto/ssh/knownhosts
|
|||
golang.org/x/net/context
|
||||
golang.org/x/net/internal/socks
|
||||
golang.org/x/net/proxy
|
||||
# golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70
|
||||
# golang.org/x/sys v0.0.0-20211031064116-611d5d643895
|
||||
## explicit
|
||||
golang.org/x/sys/cpu
|
||||
golang.org/x/sys/internal/unsafeheader
|
||||
|
|
|
|||
Loading…
Reference in a new issue