1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-05-21 22:43:27 +02:00

some refactoring in anticipation of the graph feature

This commit is contained in:
Jesse Duffield 2021-11-01 09:35:54 +11:00
parent 7a464ae5b7
commit 2fc1498517
40 changed files with 523 additions and 411 deletions

13
go.mod
View File

@ -9,20 +9,18 @@ require (
github.com/cli/safeexec v1.0.0 github.com/cli/safeexec v1.0.0
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/creack/pty v1.1.11 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/fatih/color v1.9.0 // indirect
github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify v1.4.7
github.com/go-errors/errors v1.4.1 github.com/go-errors/errors v1.4.1
github.com/go-logfmt/logfmt v0.5.0 // indirect github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/golang/protobuf v1.3.2 // indirect 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/gookit/color v1.4.2
github.com/imdario/mergo v0.3.11 github.com/imdario/mergo v0.3.11
github.com/integrii/flaggy v1.4.0 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/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/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
github.com/jesseduffield/yaml v2.1.0+incompatible github.com/jesseduffield/yaml v2.1.0+incompatible
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
@ -30,12 +28,11 @@ require (
github.com/kylelemons/godebug v1.1.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect
github.com/kyokomi/emoji/v2 v2.2.8 github.com/kyokomi/emoji/v2 v2.2.8
github.com/lucasb-eyer/go-colorful v1.2.0 github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mattn/go-colorable v0.1.7 // indirect github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13
github.com/mgutz/str v1.2.0 github.com/mgutz/str v1.2.0
github.com/onsi/ginkgo v1.10.3 // indirect github.com/onsi/ginkgo v1.10.3 // indirect
github.com/onsi/gomega v1.7.1 // 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/sahilm/fuzzy v0.1.0
github.com/sirupsen/logrus v1.4.2 github.com/sirupsen/logrus v1.4.2
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
@ -43,7 +40,7 @@ require (
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // 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/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0 gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0

45
go.sum
View File

@ -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 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 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.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= 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/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 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM=
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI= 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 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 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 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/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.20211031223253-24baf341da75 h1:zu+WBGwscCwu7GEuxANGl8E51HbW6ueqTF1XdAoqnZs=
github.com/jesseduffield/gocui v0.3.1-0.20211017035223-b68948e63cc3/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU= github.com/jesseduffield/gocui v0.3.1-0.20211031223253-24baf341da75/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/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 h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I= 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= 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/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.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.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 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.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.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.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 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.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 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/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 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 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-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-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-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-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-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-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-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-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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744/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-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA= golang.org/x/sys v0.0.0-20211031064116-611d5d643895 h1:iaNpwpnrgL5jzWS0vCNnfa8HqzxveCFpFx3uC/X4Tps=
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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/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 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=

View File

@ -33,7 +33,7 @@ func (gui *Gui) handleCopyCommit() error {
return err return err
} }
item, ok := context.SelectedItem() item, ok := context.GetSelectedItem()
if !ok { if !ok {
return nil return nil
} }

View File

@ -640,7 +640,7 @@ func (gui *Gui) handleGotoBottomForCommitsPanel() error {
} }
for _, context := range gui.getListContexts() { for _, context := range gui.getListContexts() {
if context.ViewName == "commits" { if context.GetViewName() == "commits" {
return context.handleGotoBottom() return context.handleGotoBottom()
} }
} }

View File

@ -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 // the status panel is not yet a list context (and may never be), so this method is not
// quite the same as currentSideContext() // quite the same as currentSideContext()
func (gui *Gui) currentSideListContext() *ListContext { func (gui *Gui) currentSideListContext() IListContext {
context := gui.currentSideContext() context := gui.currentSideContext()
listContext, ok := context.(*ListContext) listContext, ok := context.(IListContext)
if !ok { if !ok {
return nil return nil
} }

View File

@ -56,19 +56,19 @@ var allContextKeys = []ContextKey{
type ContextTree struct { type ContextTree struct {
Status Context Status Context
Files *ListContext Files IListContext
Submodules *ListContext Submodules IListContext
Menu *ListContext Menu IListContext
Branches *ListContext Branches IListContext
Remotes *ListContext Remotes IListContext
RemoteBranches *ListContext RemoteBranches IListContext
Tags *ListContext Tags IListContext
BranchCommits *ListContext BranchCommits IListContext
CommitFiles *ListContext CommitFiles IListContext
ReflogCommits *ListContext ReflogCommits IListContext
SubCommits *ListContext SubCommits IListContext
Stash *ListContext Stash IListContext
Suggestions *ListContext Suggestions IListContext
Normal Context Normal Context
Staging Context Staging Context
PatchBuilding Context PatchBuilding Context

View File

@ -395,9 +395,7 @@ func (gui *Gui) handleCommitPress() error {
prefix := rgx.ReplaceAllString(gui.getCheckedOutBranch().Name, prefixReplace) prefix := rgx.ReplaceAllString(gui.getCheckedOutBranch().Name, prefixReplace)
gui.Views.CommitMessage.ClearTextArea() gui.Views.CommitMessage.ClearTextArea()
gui.Views.CommitMessage.TextArea.TypeString(prefix) gui.Views.CommitMessage.TextArea.TypeString(prefix)
gui.g.Update(func(*gocui.Gui) error { gui.render()
return nil
})
} }
gui.g.Update(func(g *gocui.Gui) error { gui.g.Update(func(g *gocui.Gui) error {

View File

@ -458,6 +458,12 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *oscom
return gui, nil return gui, nil
} }
var RuneReplacements = map[rune]string{
// for the commit graph
'⏣': "M",
'⎔': "o",
}
// Run setup the gui with keybindings and start the mainloop // Run setup the gui with keybindings and start the mainloop
func (gui *Gui) Run() error { func (gui *Gui) Run() error {
recordEvents := recordingEvents() recordEvents := recordingEvents()
@ -468,7 +474,7 @@ func (gui *Gui) Run() error {
playMode = gocui.REPLAYING 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 { if err != nil {
return err return err
} }
@ -712,6 +718,7 @@ func (gui *Gui) startBackgroundFetch() {
} else { } else {
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error { gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error {
err := gui.fetch(false, "") err := gui.fetch(false, "")
gui.render()
return err return err
}) })
} }

View File

@ -52,24 +52,19 @@ func (gui *Gui) createAllViews() error {
gui.Views.Stash.Title = gui.Tr.StashTitle gui.Views.Stash.Title = gui.Tr.StashTitle
gui.Views.Stash.FgColor = theme.GocuiDefaultTextColor gui.Views.Stash.FgColor = theme.GocuiDefaultTextColor
gui.Views.Stash.ContainsList = true
gui.Views.Commits.Title = gui.Tr.CommitsTitle gui.Views.Commits.Title = gui.Tr.CommitsTitle
gui.Views.Commits.FgColor = theme.GocuiDefaultTextColor gui.Views.Commits.FgColor = theme.GocuiDefaultTextColor
gui.Views.Commits.ContainsList = true
gui.Views.CommitFiles.Title = gui.Tr.CommitFiles gui.Views.CommitFiles.Title = gui.Tr.CommitFiles
gui.Views.CommitFiles.FgColor = theme.GocuiDefaultTextColor gui.Views.CommitFiles.FgColor = theme.GocuiDefaultTextColor
gui.Views.CommitFiles.ContainsList = true
gui.Views.Branches.Title = gui.Tr.BranchesTitle gui.Views.Branches.Title = gui.Tr.BranchesTitle
gui.Views.Branches.FgColor = theme.GocuiDefaultTextColor gui.Views.Branches.FgColor = theme.GocuiDefaultTextColor
gui.Views.Branches.ContainsList = true
gui.Views.Files.Highlight = true gui.Views.Files.Highlight = true
gui.Views.Files.Title = gui.Tr.FilesTitle gui.Views.Files.Title = gui.Tr.FilesTitle
gui.Views.Files.FgColor = theme.GocuiDefaultTextColor gui.Views.Files.FgColor = theme.GocuiDefaultTextColor
gui.Views.Files.ContainsList = true
gui.Views.Secondary.Title = gui.Tr.DiffTitle gui.Views.Secondary.Title = gui.Tr.DiffTitle
gui.Views.Secondary.Wrap = true gui.Views.Secondary.Wrap = true
@ -111,7 +106,6 @@ func (gui *Gui) createAllViews() error {
gui.Views.Credentials.Editable = true gui.Views.Credentials.Editable = true
gui.Views.Suggestions.Visible = false gui.Views.Suggestions.Visible = false
gui.Views.Suggestions.ContainsList = true
gui.Views.Menu.Visible = false gui.Views.Menu.Visible = false
@ -264,7 +258,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
} }
for _, listContext := range gui.getListContexts() { for _, listContext := range gui.getListContexts() {
view, err := gui.g.View(listContext.ViewName) view, err := gui.g.View(listContext.GetViewName())
if err != nil { if err != nil {
continue continue
} }
@ -274,8 +268,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
continue continue
} }
// check if the selected line is now out of view and if so refocus it listContext.FocusLine()
view.FocusPoint(0, listContext.GetPanelState().GetSelectedLineIdx())
view.SelBgColor = theme.GocuiSelectedLineBgColor view.SelBgColor = theme.GocuiSelectedLineBgColor

View File

@ -1,21 +1,49 @@
package gui package gui
import (
"fmt"
)
type ListContext struct { type ListContext struct {
GetItemsLength func() int GetItemsLength func() int
GetDisplayStrings func() [][]string GetDisplayStrings func(startIdx int, length int) [][]string
OnFocus func() error OnFocus func() error
OnFocusLost func() error OnFocusLost func() error
OnClickSelectedItem 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) // 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) SelectedItem func() (ListItem, bool)
GetPanelState func() IListPanelState OnGetPanelState func() IListPanelState
Gui *Gui Gui *Gui
*BasicContext *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 { type IListPanelState interface {
SetSelectedLineIdx(int) SetSelectedLineIdx(int)
GetSelectedLineIdx() int GetSelectedLineIdx() int
@ -29,12 +57,26 @@ type ListItem interface {
Description() string Description() string
} }
func (lc *ListContext) GetSelectedItem() (ListItem, bool) { func (self *ListContext) FocusLine() {
return lc.SelectedItem() 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 { func formatListFooter(selectedLineIdx int, length int) string {
item, ok := lc.SelectedItem() 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 { if !ok {
return "" 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 // 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 { func (self *ListContext) OnRender() error {
view, err := lc.Gui.g.View(lc.ViewName) view, err := self.Gui.g.View(self.ViewName)
if err != nil { if err != nil {
return nil return nil
} }
if lc.GetDisplayStrings != nil { if self.GetDisplayStrings != nil {
lc.Gui.refreshSelectedLine(lc.GetPanelState(), lc.GetItemsLength()) self.Gui.refreshSelectedLine(self.GetPanelState(), self.GetItemsLength())
lc.Gui.renderDisplayStrings(view, lc.GetDisplayStrings()) self.Gui.renderDisplayStrings(view, self.GetDisplayStrings(0, self.GetItemsLength()))
self.Gui.render()
} }
return nil return nil
} }
func (lc *ListContext) HandleFocusLost() error { func (self *ListContext) HandleFocusLost() error {
if lc.OnFocusLost != nil { if self.OnFocusLost != nil {
return lc.OnFocusLost() return self.OnFocusLost()
} }
return nil return nil
} }
func (lc *ListContext) HandleFocus() error { func (self *ListContext) HandleFocus() error {
if lc.Gui.popupPanelFocused() { if self.Gui.popupPanelFocused() {
return nil return nil
} }
view, err := lc.Gui.g.View(lc.ViewName) self.FocusLine()
if self.Gui.State.Modes.Diffing.Active() {
return self.Gui.renderDiff()
}
if self.OnFocus != nil {
return self.OnFocus()
}
return nil
}
func (self *ListContext) HandleRender() error {
return self.OnRender()
}
func (self *ListContext) handlePrevLine() error {
return self.handleLineChange(-1)
}
func (self *ListContext) handleNextLine() error {
return self.handleLineChange(1)
}
func (self *ListContext) handleLineChange(change int) error {
if !self.Gui.isPopupPanel(self.ViewName) && self.Gui.popupPanelFocused() {
return nil
}
selectedLineIdx := self.GetPanelState().GetSelectedLineIdx()
if (change < 0 && selectedLineIdx == 0) || (change > 0 && selectedLineIdx == self.GetItemsLength()-1) {
return nil
}
self.Gui.changeSelectedLine(self.GetPanelState(), self.GetItemsLength(), change)
return self.HandleFocus()
}
func (self *ListContext) handleNextPage() error {
view, err := self.Gui.g.View(self.ViewName)
if err != nil {
return nil
}
delta := self.Gui.pageDelta(view)
return self.handleLineChange(delta)
}
func (self *ListContext) handleGotoTop() error {
return self.handleLineChange(-self.GetItemsLength())
}
func (self *ListContext) handleGotoBottom() error {
return self.handleLineChange(self.GetItemsLength())
}
func (self *ListContext) handlePrevPage() error {
view, err := self.Gui.g.View(self.ViewName)
if err != nil { if err != nil {
return nil return nil
} }
view.FocusPoint(0, lc.GetPanelState().GetSelectedLineIdx()) delta := self.Gui.pageDelta(view)
if lc.Gui.State.Modes.Diffing.Active() { return self.handleLineChange(-delta)
return lc.Gui.renderDiff()
}
if lc.OnFocus != nil {
return lc.OnFocus()
}
return nil
} }
func (lc *ListContext) HandleRender() error { func (self *ListContext) handleClick() error {
return lc.OnRender() if !self.Gui.isPopupPanel(self.ViewName) && self.Gui.popupPanelFocused() {
}
func (lc *ListContext) handlePrevLine() error {
return lc.handleLineChange(-1)
}
func (lc *ListContext) handleNextLine() error {
return lc.handleLineChange(1)
}
func (lc *ListContext) handleLineChange(change int) error {
if !lc.Gui.isPopupPanel(lc.ViewName) && lc.Gui.popupPanelFocused() {
return nil return nil
} }
view, err := lc.Gui.g.View(lc.ViewName) view, err := self.Gui.g.View(self.ViewName)
if err != nil {
return err
}
selectedLineIdx := lc.GetPanelState().GetSelectedLineIdx()
if (change < 0 && selectedLineIdx == 0) || (change > 0 && selectedLineIdx == lc.GetItemsLength()-1) {
return nil
}
lc.Gui.changeSelectedLine(lc.GetPanelState(), lc.GetItemsLength(), change)
view.FocusPoint(0, lc.GetPanelState().GetSelectedLineIdx())
return lc.HandleFocus()
}
func (lc *ListContext) handleNextPage() error {
view, err := lc.Gui.g.View(lc.ViewName)
if err != nil {
return nil
}
delta := lc.Gui.pageDelta(view)
return lc.handleLineChange(delta)
}
func (lc *ListContext) handleGotoTop() error {
return lc.handleLineChange(-lc.GetItemsLength())
}
func (lc *ListContext) handleGotoBottom() error {
return lc.handleLineChange(lc.GetItemsLength())
}
func (lc *ListContext) handlePrevPage() error {
view, err := lc.Gui.g.View(lc.ViewName)
if err != nil { if err != nil {
return nil return nil
} }
delta := lc.Gui.pageDelta(view) prevSelectedLineIdx := self.GetPanelState().GetSelectedLineIdx()
return lc.handleLineChange(-delta)
}
func (lc *ListContext) handleClick() error {
if !lc.Gui.isPopupPanel(lc.ViewName) && lc.Gui.popupPanelFocused() {
return nil
}
view, err := lc.Gui.g.View(lc.ViewName)
if err != nil {
return nil
}
prevSelectedLineIdx := lc.GetPanelState().GetSelectedLineIdx()
newSelectedLineIdx := view.SelectedLineIdx() newSelectedLineIdx := view.SelectedLineIdx()
// we need to focus the view // we need to focus the view
if err := lc.Gui.pushContext(lc); err != nil { if err := self.Gui.pushContext(self); err != nil {
return err return err
} }
if newSelectedLineIdx > lc.GetItemsLength()-1 { if newSelectedLineIdx > self.GetItemsLength()-1 {
return nil return nil
} }
lc.GetPanelState().SetSelectedLineIdx(newSelectedLineIdx) self.GetPanelState().SetSelectedLineIdx(newSelectedLineIdx)
prevViewName := lc.Gui.currentViewName() prevViewName := self.Gui.currentViewName()
if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == lc.ViewName && lc.OnClickSelectedItem != nil { if prevSelectedLineIdx == newSelectedLineIdx && prevViewName == self.ViewName && self.OnClickSelectedItem != nil {
return lc.OnClickSelectedItem() return self.OnClickSelectedItem()
} }
return lc.HandleFocus() return self.HandleFocus()
} }
func (lc *ListContext) onSearchSelect(selectedLineIdx int) error { func (self *ListContext) onSearchSelect(selectedLineIdx int) error {
lc.GetPanelState().SetSelectedLineIdx(selectedLineIdx) self.GetPanelState().SetSelectedLineIdx(selectedLineIdx)
return lc.HandleFocus() return self.HandleFocus()
} }

View File

@ -6,7 +6,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
) )
func (gui *Gui) menuListContext() *ListContext { func (gui *Gui) menuListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "menu", ViewName: "menu",
@ -15,7 +15,7 @@ func (gui *Gui) menuListContext() *ListContext {
OnGetOptionsMap: gui.getMenuOptions, OnGetOptionsMap: gui.getMenuOptions,
}, },
GetItemsLength: func() int { return gui.Views.Menu.LinesHeight() }, 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, OnFocus: gui.handleMenuSelect,
OnClickSelectedItem: gui.onMenuPress, OnClickSelectedItem: gui.onMenuPress,
Gui: gui, Gui: gui,
@ -24,7 +24,7 @@ func (gui *Gui) menuListContext() *ListContext {
} }
} }
func (gui *Gui) filesListContext() *ListContext { func (gui *Gui) filesListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "files", ViewName: "files",
@ -33,11 +33,11 @@ func (gui *Gui) filesListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return gui.State.FileManager.GetItemsLength() }, 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, OnFocus: gui.focusAndSelectFile,
OnClickSelectedItem: gui.handleFilePress, OnClickSelectedItem: gui.handleFilePress,
Gui: gui, 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) lines := gui.State.FileManager.Render(gui.State.Modes.Diffing.Ref, gui.State.Submodules)
mappedLines := make([][]string, len(lines)) mappedLines := make([][]string, len(lines))
for i, line := range 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{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "branches", ViewName: "branches",
@ -62,10 +62,10 @@ func (gui *Gui) branchesListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.Branches) }, GetItemsLength: func() int { return len(gui.State.Branches) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Branches }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.Branches },
OnFocus: gui.handleBranchSelect, OnFocus: gui.handleBranchSelect,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetBranchListDisplayStrings(gui.State.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref) return presentation.GetBranchListDisplayStrings(gui.State.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref)
}, },
SelectedItem: func() (ListItem, bool) { SelectedItem: func() (ListItem, bool) {
@ -75,7 +75,7 @@ func (gui *Gui) branchesListContext() *ListContext {
} }
} }
func (gui *Gui) remotesListContext() *ListContext { func (gui *Gui) remotesListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "branches", ViewName: "branches",
@ -84,11 +84,11 @@ func (gui *Gui) remotesListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.Remotes) }, 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, OnFocus: gui.handleRemoteSelect,
OnClickSelectedItem: gui.handleRemoteEnter, OnClickSelectedItem: gui.handleRemoteEnter,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetRemoteListDisplayStrings(gui.State.Remotes, gui.State.Modes.Diffing.Ref) return presentation.GetRemoteListDisplayStrings(gui.State.Remotes, gui.State.Modes.Diffing.Ref)
}, },
SelectedItem: func() (ListItem, bool) { SelectedItem: func() (ListItem, bool) {
@ -98,7 +98,7 @@ func (gui *Gui) remotesListContext() *ListContext {
} }
} }
func (gui *Gui) remoteBranchesListContext() *ListContext { func (gui *Gui) remoteBranchesListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "branches", ViewName: "branches",
@ -107,10 +107,10 @@ func (gui *Gui) remoteBranchesListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.RemoteBranches) }, GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
GetPanelState: func() IListPanelState { return gui.State.Panels.RemoteBranches }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.RemoteBranches },
OnFocus: gui.handleRemoteBranchSelect, OnFocus: gui.handleRemoteBranchSelect,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetRemoteBranchListDisplayStrings(gui.State.RemoteBranches, gui.State.Modes.Diffing.Ref) return presentation.GetRemoteBranchListDisplayStrings(gui.State.RemoteBranches, gui.State.Modes.Diffing.Ref)
}, },
SelectedItem: func() (ListItem, bool) { SelectedItem: func() (ListItem, bool) {
@ -120,7 +120,7 @@ func (gui *Gui) remoteBranchesListContext() *ListContext {
} }
} }
func (gui *Gui) tagsListContext() *ListContext { func (gui *Gui) tagsListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "branches", ViewName: "branches",
@ -129,10 +129,10 @@ func (gui *Gui) tagsListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.Tags) }, GetItemsLength: func() int { return len(gui.State.Tags) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Tags }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.Tags },
OnFocus: gui.handleTagSelect, OnFocus: gui.handleTagSelect,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetTagListDisplayStrings(gui.State.Tags, gui.State.Modes.Diffing.Ref) return presentation.GetTagListDisplayStrings(gui.State.Tags, gui.State.Modes.Diffing.Ref)
}, },
SelectedItem: func() (ListItem, bool) { SelectedItem: func() (ListItem, bool) {
@ -142,7 +142,7 @@ func (gui *Gui) tagsListContext() *ListContext {
} }
} }
func (gui *Gui) branchCommitsListContext() *ListContext { func (gui *Gui) branchCommitsListContext() IListContext {
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
@ -152,11 +152,11 @@ func (gui *Gui) branchCommitsListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.Commits) }, 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, OnFocus: gui.handleCommitSelect,
OnClickSelectedItem: gui.handleViewCommitFiles, OnClickSelectedItem: gui.handleViewCommitFiles,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetCommitListDisplayStrings( return presentation.GetCommitListDisplayStrings(
gui.State.Commits, gui.State.Commits,
gui.State.ScreenMode != SCREEN_NORMAL, gui.State.ScreenMode != SCREEN_NORMAL,
@ -172,7 +172,7 @@ func (gui *Gui) branchCommitsListContext() *ListContext {
} }
} }
func (gui *Gui) reflogCommitsListContext() *ListContext { func (gui *Gui) reflogCommitsListContext() IListContext {
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
@ -182,10 +182,10 @@ func (gui *Gui) reflogCommitsListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) }, GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
GetPanelState: func() IListPanelState { return gui.State.Panels.ReflogCommits }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.ReflogCommits },
OnFocus: gui.handleReflogCommitSelect, OnFocus: gui.handleReflogCommitSelect,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetReflogCommitListDisplayStrings( return presentation.GetReflogCommitListDisplayStrings(
gui.State.FilteredReflogCommits, gui.State.FilteredReflogCommits,
gui.State.ScreenMode != SCREEN_NORMAL, gui.State.ScreenMode != SCREEN_NORMAL,
@ -201,7 +201,7 @@ func (gui *Gui) reflogCommitsListContext() *ListContext {
} }
} }
func (gui *Gui) subCommitsListContext() *ListContext { func (gui *Gui) subCommitsListContext() IListContext {
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
@ -211,10 +211,10 @@ func (gui *Gui) subCommitsListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.SubCommits) }, GetItemsLength: func() int { return len(gui.State.SubCommits) },
GetPanelState: func() IListPanelState { return gui.State.Panels.SubCommits }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.SubCommits },
OnFocus: gui.handleSubCommitSelect, OnFocus: gui.handleSubCommitSelect,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetCommitListDisplayStrings( return presentation.GetCommitListDisplayStrings(
gui.State.SubCommits, gui.State.SubCommits,
gui.State.ScreenMode != SCREEN_NORMAL, gui.State.ScreenMode != SCREEN_NORMAL,
@ -230,7 +230,7 @@ func (gui *Gui) subCommitsListContext() *ListContext {
} }
} }
func (gui *Gui) stashListContext() *ListContext { func (gui *Gui) stashListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "stash", ViewName: "stash",
@ -239,10 +239,10 @@ func (gui *Gui) stashListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.StashEntries) }, GetItemsLength: func() int { return len(gui.State.StashEntries) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Stash }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.Stash },
OnFocus: gui.handleStashEntrySelect, OnFocus: gui.handleStashEntrySelect,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetStashEntryListDisplayStrings(gui.State.StashEntries, gui.State.Modes.Diffing.Ref) return presentation.GetStashEntryListDisplayStrings(gui.State.StashEntries, gui.State.Modes.Diffing.Ref)
}, },
SelectedItem: func() (ListItem, bool) { SelectedItem: func() (ListItem, bool) {
@ -252,7 +252,7 @@ func (gui *Gui) stashListContext() *ListContext {
} }
} }
func (gui *Gui) commitFilesListContext() *ListContext { func (gui *Gui) commitFilesListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "commitFiles", ViewName: "commitFiles",
@ -261,10 +261,10 @@ func (gui *Gui) commitFilesListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return gui.State.CommitFileManager.GetItemsLength() }, GetItemsLength: func() int { return gui.State.CommitFileManager.GetItemsLength() },
GetPanelState: func() IListPanelState { return gui.State.Panels.CommitFiles }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.CommitFiles },
OnFocus: gui.handleCommitFileSelect, OnFocus: gui.handleCommitFileSelect,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
if gui.State.CommitFileManager.GetItemsLength() == 0 { if gui.State.CommitFileManager.GetItemsLength() == 0 {
return [][]string{{style.FgRed.Sprint("(none)")}} return [][]string{{style.FgRed.Sprint("(none)")}}
} }
@ -284,7 +284,7 @@ func (gui *Gui) commitFilesListContext() *ListContext {
} }
} }
func (gui *Gui) submodulesListContext() *ListContext { func (gui *Gui) submodulesListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "files", ViewName: "files",
@ -293,10 +293,10 @@ func (gui *Gui) submodulesListContext() *ListContext {
Kind: SIDE_CONTEXT, Kind: SIDE_CONTEXT,
}, },
GetItemsLength: func() int { return len(gui.State.Submodules) }, GetItemsLength: func() int { return len(gui.State.Submodules) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Submodules }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.Submodules },
OnFocus: gui.handleSubmoduleSelect, OnFocus: gui.handleSubmoduleSelect,
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetSubmoduleListDisplayStrings(gui.State.Submodules) return presentation.GetSubmoduleListDisplayStrings(gui.State.Submodules)
}, },
SelectedItem: func() (ListItem, bool) { SelectedItem: func() (ListItem, bool) {
@ -306,7 +306,7 @@ func (gui *Gui) submodulesListContext() *ListContext {
} }
} }
func (gui *Gui) suggestionsListContext() *ListContext { func (gui *Gui) suggestionsListContext() IListContext {
return &ListContext{ return &ListContext{
BasicContext: &BasicContext{ BasicContext: &BasicContext{
ViewName: "suggestions", ViewName: "suggestions",
@ -315,17 +315,17 @@ func (gui *Gui) suggestionsListContext() *ListContext {
Kind: PERSISTENT_POPUP, Kind: PERSISTENT_POPUP,
}, },
GetItemsLength: func() int { return len(gui.State.Suggestions) }, GetItemsLength: func() int { return len(gui.State.Suggestions) },
GetPanelState: func() IListPanelState { return gui.State.Panels.Suggestions }, OnGetPanelState: func() IListPanelState { return gui.State.Panels.Suggestions },
OnFocus: func() error { return nil }, OnFocus: func() error { return nil },
Gui: gui, Gui: gui,
GetDisplayStrings: func() [][]string { GetDisplayStrings: func(startIdx int, length int) [][]string {
return presentation.GetSuggestionListDisplayStrings(gui.State.Suggestions) return presentation.GetSuggestionListDisplayStrings(gui.State.Suggestions)
}, },
} }
} }
func (gui *Gui) getListContexts() []*ListContext { func (gui *Gui) getListContexts() []IListContext {
return []*ListContext{ return []IListContext{
gui.State.Contexts.Menu, gui.State.Contexts.Menu,
gui.State.Contexts.Files, gui.State.Contexts.Files,
gui.State.Contexts.Branches, gui.State.Contexts.Branches,
@ -333,7 +333,6 @@ func (gui *Gui) getListContexts() []*ListContext {
gui.State.Contexts.RemoteBranches, gui.State.Contexts.RemoteBranches,
gui.State.Contexts.Tags, gui.State.Contexts.Tags,
gui.State.Contexts.BranchCommits, gui.State.Contexts.BranchCommits,
gui.State.Contexts.BranchCommits,
gui.State.Contexts.ReflogCommits, gui.State.Contexts.ReflogCommits,
gui.State.Contexts.SubCommits, gui.State.Contexts.SubCommits,
gui.State.Contexts.Stash, gui.State.Contexts.Stash,
@ -352,38 +351,38 @@ func (gui *Gui) getListContextKeyBindings() []*Binding {
listContext := listContext listContext := listContext
bindings = append(bindings, []*Binding{ 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.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, 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.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, 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.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, 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.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, 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.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, 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.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.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.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.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.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.ViewName, Tag: "navigation", Contexts: []string{string(listContext.Key)}, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: listContext.handleNextLine}, {ViewName: listContext.GetViewName(), Tag: "navigation", Contexts: []string{string(listContext.GetKey())}, 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(), 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 // the commits panel needs to lazyload things so it has a couple of its own handlers
openSearchHandler := gui.handleOpenSearch openSearchHandler := gui.handleOpenSearch
gotoBottomHandler := listContext.handleGotoBottom gotoBottomHandler := listContext.handleGotoBottom
if listContext.ViewName == "commits" { if listContext.GetViewName() == "commits" {
openSearchHandler = gui.handleOpenSearchForCommitsPanel openSearchHandler = gui.handleOpenSearchForCommitsPanel
gotoBottomHandler = gui.handleGotoBottomForCommitsPanel gotoBottomHandler = gui.handleGotoBottomForCommitsPanel
} }
bindings = append(bindings, []*Binding{ bindings = append(bindings, []*Binding{
{ {
ViewName: listContext.ViewName, ViewName: listContext.GetViewName(),
Contexts: []string{string(listContext.Key)}, Contexts: []string{string(listContext.GetKey())},
Key: gui.getKey(keybindingConfig.Universal.StartSearch), Key: gui.getKey(keybindingConfig.Universal.StartSearch),
Handler: func() error { return openSearchHandler(listContext.ViewName) }, Handler: func() error { return openSearchHandler(listContext.GetViewName()) },
Description: gui.Tr.LcStartSearch, Description: gui.Tr.LcStartSearch,
Tag: "navigation", Tag: "navigation",
}, },
{ {
ViewName: listContext.ViewName, ViewName: listContext.GetViewName(),
Contexts: []string{string(listContext.Key)}, Contexts: []string{string(listContext.GetKey())},
Key: gui.getKey(keybindingConfig.Universal.GotoBottom), Key: gui.getKey(keybindingConfig.Universal.GotoBottom),
Handler: gotoBottomHandler, Handler: gotoBottomHandler,
Description: gui.Tr.LcGotoBottom, Description: gui.Tr.LcGotoBottom,

View File

@ -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, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0)
menuView.Title = title menuView.Title = title
menuView.FgColor = theme.GocuiDefaultTextColor menuView.FgColor = theme.GocuiDefaultTextColor
menuView.ContainsList = true
menuView.Clear()
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error { menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
return nil return nil
})) }))
fmt.Fprint(menuView, list) menuView.SetContent(list)
gui.State.Panels.Menu.SelectedLineIdx = 0 gui.State.Panels.Menu.SelectedLineIdx = 0
gui.g.Update(func(g *gocui.Gui) error { gui.g.Update(func(g *gocui.Gui) error {

View File

@ -90,9 +90,8 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager {
view.Reset() view.Reset()
}, },
func() { func() {
gui.g.Update(func(*gocui.Gui) error { // gui.g.Draw(view) // doing this causes an issue when there's a popup panel in front of the main view.
return nil gui.render()
})
}, },
func() { func() {
// Need to check if the content of the view is well past the origin. // Need to check if the content of the view is well past the origin.

View File

@ -202,8 +202,7 @@ func (gui *Gui) cleanString(s string) string {
} }
func (gui *Gui) setViewContentSync(v *gocui.View, s string) { func (gui *Gui) setViewContentSync(v *gocui.View, s string) {
v.Clear() v.SetContent(gui.cleanString(s))
fmt.Fprint(v, gui.cleanString(s))
} }
func (gui *Gui) setViewContent(v *gocui.View, s string) { 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) { func (gui *Gui) renderDisplayStrings(v *gocui.View, displayStrings [][]string) {
gui.g.Update(func(g *gocui.Gui) error {
list := utils.RenderDisplayStrings(displayStrings) list := utils.RenderDisplayStrings(displayStrings)
v.Clear() v.SetContent(list)
fmt.Fprint(v, list)
return nil
})
} }
func (gui *Gui) globalOptionsMap() map[string]string { func (gui *Gui) globalOptionsMap() map[string]string {
@ -388,3 +383,7 @@ func getTabbedView(gui *Gui) *gocui.View {
view, _ := gui.g.View(context.GetViewName()) view, _ := gui.g.View(context.GetViewName())
return view return view
} }
func (gui *Gui) render() {
gui.g.Update(func(g *gocui.Gui) error { return nil })
}

View File

@ -131,7 +131,6 @@ func (m *ViewBufferManager) NewCmdTask(r io.Reader, cmd *exec.Cmd, prefix string
select { select {
case <-stop: case <-stop:
m.refreshView()
break outer break outer
default: 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 // 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 // so we're at the EOF and can flush the stale content
m.onEndOfInput() m.onEndOfInput()
m.refreshView()
break outer break outer
} }
_, _ = m.writer.Write(append(scanner.Bytes(), '\n')) _, _ = m.writer.Write(append(scanner.Bytes(), '\n'))
} }
m.refreshView() m.refreshView()
case <-stop: case <-stop:
m.refreshView()
break outer break outer
} }
} }
m.refreshView()
if err := cmd.Wait(); err != nil { if err := cmd.Wait(); err != nil {
// it's fine if we've killed this program ourselves // it's fine if we've killed this program ourselves
if !strings.Contains(err.Error(), "signal: killed") { 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 { if onDone != nil {
onDone() onDone()
} }

View File

@ -8,6 +8,7 @@ import (
"path/filepath" "path/filepath"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/integration" "github.com/jesseduffield/lazygit/pkg/integration"
"github.com/jesseduffield/lazygit/pkg/secureexec" "github.com/jesseduffield/lazygit/pkg/secureexec"
@ -66,7 +67,7 @@ func main() {
app := &App{testDir: testDir} app := &App{testDir: testDir}
app.loadTests() 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 { if err != nil {
log.Panicln(err) log.Panicln(err)
} }

View File

@ -6,7 +6,6 @@ package gocui
import ( import (
standardErrors "errors" standardErrors "errors"
"fmt"
"log" "log"
"runtime" "runtime"
"strings" "strings"
@ -76,6 +75,8 @@ type GuiMutexes struct {
tickingMutex sync.Mutex tickingMutex sync.Mutex
ViewsMutex sync.Mutex ViewsMutex sync.Mutex
drawMutex sync.Mutex
} }
type PlayMode int type PlayMode int
@ -166,14 +167,14 @@ type Gui struct {
} }
// NewGui returns a new Gui object with a given output mode. // 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{} g := &Gui{}
var err error var err error
if headless { if headless {
err = g.tcellInitSimulation() err = g.tcellInitSimulation()
} else { } else {
err = g.tcellInit() err = g.tcellInit(runeReplacements)
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -664,79 +665,6 @@ func (g *Gui) onResize() {
// g.screen.Sync() // 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) { func (g *Gui) clear(fg, bg Attribute) (int, int) {
st := getTcellStyle(oldStyle{fg: fg, bg: bg, outputMode: g.outputMode}) st := getTcellStyle(oldStyle{fg: fg, bg: bg, outputMode: g.outputMode})
w, h := Screen.Size() w, h := Screen.Size()
@ -983,7 +911,7 @@ func (g *Gui) drawListFooter(v *View, fgColor, bgColor Attribute) error {
return nil 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 { if v.y1 < 0 || v.y1 >= g.maxY {
return nil return nil
@ -1006,12 +934,102 @@ func (g *Gui) drawListFooter(v *View, fgColor, bgColor Attribute) error {
return nil 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. // draw manages the cursor and calls the draw function of a view.
func (g *Gui) draw(v *View) error { func (g *Gui) draw(v *View) error {
if g.suspended { if g.suspended {
return nil 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 g.Cursor {
if curview := g.currentView; curview != nil { if curview := g.currentView; curview != nil {
vMaxX, vMaxY := curview.Size() vMaxX, vMaxY := curview.Size()

View File

@ -26,6 +26,10 @@ var runeReplacements = map[rune]string{
'┐': "+", '┐': "+",
'└': "+", '└': "+",
'┘': "+", '┘': "+",
'╭': "+",
'╮': "+",
'╰': "+",
'╯': "+",
'─': "-", '─': "-",
// using a hyphen here actually looks weird. // using a hyphen here actually looks weird.
@ -33,6 +37,9 @@ var runeReplacements = map[rune]string{
'╶': " ", '╶': " ",
'╴': " ", '╴': " ",
'┴': "+",
'┬': "+",
'╷': "|",
'├': "+", '├': "+",
'│': "|", '│': "|",
'▼': "v", '▼': "v",
@ -42,7 +49,7 @@ var runeReplacements = map[rune]string{
} }
// tcellInit initializes tcell screen for use. // tcellInit initializes tcell screen for use.
func (g *Gui) tcellInit() error { func (g *Gui) tcellInit(runeReplacements map[rune]string) error {
runewidth.DefaultCondition.EastAsianWidth = false runewidth.DefaultCondition.EastAsianWidth = false
tcell.SetEncodingFallback(tcell.EncodingFallbackASCII) tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)
@ -51,7 +58,7 @@ func (g *Gui) tcellInit() error {
} else if e = s.Init(); e != nil { } else if e = s.Init(); e != nil {
return e return e
} else { } else {
registerRuneFallbacks(s) registerRuneFallbacks(s, runeReplacements)
g.screen = s g.screen = s
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 { for before, after := range runeReplacements {
s.RegisterRuneFallback(before, after) s.RegisterRuneFallback(before, after)
} }
for before, after := range additional {
s.RegisterRuneFallback(before, after)
}
} }
// tcellInitSimulation initializes tcell screen for use. // tcellInitSimulation initializes tcell screen for use.

View File

@ -153,14 +153,14 @@ type View struct {
searcher *searcher 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 // KeybindOnEdit should be set to true when you want to execute keybindings even when the view is editable
// (this is usually not the case) // (this is usually not the case)
KeybindOnEdit bool KeybindOnEdit bool
TextArea *TextArea 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 // 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 // if a view has a frame, that leaves less space for its writeable area
func (v *View) InnerWidth() int { 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 { 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 { func (v *View) frameOffset() int {
@ -563,8 +573,6 @@ func (v *View) Write(p []byte) (n int, err error) {
v.writeMutex.Lock() v.writeMutex.Lock()
defer v.writeMutex.Unlock() defer v.writeMutex.Unlock()
v.tainted = true
v.makeWriteable(v.wx, v.wy)
v.writeRunes(bytes.Runes(p)) v.writeRunes(bytes.Runes(p))
return len(p), nil return len(p), nil
@ -574,20 +582,16 @@ func (v *View) WriteRunes(p []rune) {
v.writeMutex.Lock() v.writeMutex.Lock()
defer v.writeMutex.Unlock() 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 v.tainted = true
// Fill with empty cells, if writing outside current view buffer // Fill with empty cells, if writing outside current view buffer
v.makeWriteable(v.wx, v.wy) 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 { for _, r := range p {
switch r { switch r {
case '\n': 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 // 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 // while processing ESC sequences. Otherwise, it returns a cell slice that
// contains the processed data. // contains the processed data.
@ -696,16 +710,28 @@ func (v *View) Read(p []byte) (n int, err error) {
return offset, io.EOF 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. // Clear empties the view's internal buffer.
// And resets reading and writing offsets. // And resets reading and writing offsets.
func (v *View) Clear() { func (v *View) Clear() {
v.writeMutex.Lock() v.writeMutex.Lock()
defer v.writeMutex.Unlock() defer v.writeMutex.Unlock()
v.rewind() v.clear()
v.lines = nil }
v.clearViewLines()
v.clearRunes() 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). // 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. // draw re-draws the view's contents.
func (v *View) draw() error { func (v *View) draw() error {
v.writeMutex.Lock()
defer v.writeMutex.Unlock()
v.clearRunes() v.clearRunes()
if !v.Visible { if !v.Visible {

View File

@ -1,6 +1,6 @@
# go-colorable # go-colorable
[![Build Status](https://travis-ci.org/mattn/go-colorable.svg?branch=master)](https://travis-ci.org/mattn/go-colorable) [![Build Status](https://github.com/mattn/go-colorable/workflows/test/badge.svg)](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest)
[![Codecov](https://codecov.io/gh/mattn/go-colorable/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-colorable) [![Codecov](https://codecov.io/gh/mattn/go-colorable/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-colorable)
[![GoDoc](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable) [![GoDoc](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable)
[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable) [![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable)

View File

@ -1,3 +1,4 @@
//go:build appengine
// +build appengine // +build appengine
package colorable package colorable

View File

@ -1,5 +1,5 @@
// +build !windows //go:build !windows && !appengine
// +build !appengine // +build !windows,!appengine
package colorable package colorable

View File

@ -1,5 +1,5 @@
// +build windows //go:build windows && !appengine
// +build !appengine // +build windows,!appengine
package colorable package colorable
@ -452,18 +452,22 @@ func (w *Writer) Write(data []byte) (n int, err error) {
} else { } else {
er = bytes.NewReader(data) er = bytes.NewReader(data)
} }
var bw [1]byte var plaintext bytes.Buffer
loop: loop:
for { for {
c1, err := er.ReadByte() c1, err := er.ReadByte()
if err != nil { if err != nil {
plaintext.WriteTo(w.out)
break loop break loop
} }
if c1 != 0x1b { if c1 != 0x1b {
bw[0] = c1 plaintext.WriteByte(c1)
w.out.Write(bw[:])
continue continue
} }
_, err = plaintext.WriteTo(w.out)
if err != nil {
break loop
}
c2, err := er.ReadByte() c2, err := er.ReadByte()
if err != nil { if err != nil {
break loop break loop
@ -719,7 +723,7 @@ loop:
n256setup() n256setup()
} }
attr &= backgroundMask attr &= backgroundMask
attr |= n256foreAttr[n256] attr |= n256foreAttr[n256%len(n256foreAttr)]
i += 2 i += 2
} }
} else if len(token) == 5 && token[i+1] == "2" { } else if len(token) == 5 && token[i+1] == "2" {
@ -761,7 +765,7 @@ loop:
n256setup() n256setup()
} }
attr &= foregroundMask attr &= foregroundMask
attr |= n256backAttr[n256] attr |= n256backAttr[n256%len(n256backAttr)]
i += 2 i += 2
} }
} else if len(token) == 5 && token[i+1] == "2" { } else if len(token) == 5 && token[i+1] == "2" {

View File

@ -1,8 +1,8 @@
module github.com/mattn/go-colorable module github.com/mattn/go-colorable
require ( require (
github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-isatty v0.0.14
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
) )
go 1.13 go 1.13

View File

@ -1,5 +1,5 @@
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -18,18 +18,22 @@ func NewNonColorable(w io.Writer) io.Writer {
// Write writes data on console // Write writes data on console
func (w *NonColorable) Write(data []byte) (n int, err error) { func (w *NonColorable) Write(data []byte) (n int, err error) {
er := bytes.NewReader(data) er := bytes.NewReader(data)
var bw [1]byte var plaintext bytes.Buffer
loop: loop:
for { for {
c1, err := er.ReadByte() c1, err := er.ReadByte()
if err != nil { if err != nil {
plaintext.WriteTo(w.out)
break loop break loop
} }
if c1 != 0x1b { if c1 != 0x1b {
bw[0] = c1 plaintext.WriteByte(c1)
w.out.Write(bw[:])
continue continue
} }
_, err = plaintext.WriteTo(w.out)
if err != nil {
break loop
}
c2, err := er.ReadByte() c2, err := er.ReadByte()
if err != nil { if err != nil {
break loop break loop

View File

@ -2,4 +2,4 @@ module github.com/mattn/go-isatty
go 1.12 go 1.12
require golang.org/x/sys v0.0.0-20200116001909-b77594299b42 require golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c

View File

@ -1,3 +1,4 @@
//go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine
// +build darwin freebsd openbsd netbsd dragonfly // +build darwin freebsd openbsd netbsd dragonfly
// +build !appengine // +build !appengine

View File

@ -1,4 +1,5 @@
// +build appengine js nacl //go:build appengine || js || nacl || wasm
// +build appengine js nacl wasm
package isatty package isatty

View File

@ -1,3 +1,4 @@
//go:build plan9
// +build plan9 // +build plan9
package isatty package isatty

View File

@ -1,5 +1,5 @@
// +build solaris //go:build solaris && !appengine
// +build !appengine // +build solaris,!appengine
package isatty package isatty
@ -8,10 +8,9 @@ import (
) )
// IsTerminal returns true if the given file descriptor is a terminal. // 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 { func IsTerminal(fd uintptr) bool {
var termio unix.Termio _, err := unix.IoctlGetTermio(int(fd), unix.TCGETA)
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
return err == nil return err == nil
} }

View File

@ -1,4 +1,5 @@
// +build linux aix //go:build (linux || aix || zos) && !appengine
// +build linux aix zos
// +build !appengine // +build !appengine
package isatty package isatty

View File

@ -1,5 +1,5 @@
// +build windows //go:build windows && !appengine
// +build !appengine // +build windows,!appengine
package isatty 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 // 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 // guys are using Windows XP, this is a workaround for those guys, it will also work on system from
// Windows vista to 10 // Windows vista to 10
// see https://stackoverflow.com/a/18792477 for details // see https://stackoverflow.com/a/18792477 for details

View File

@ -1,8 +0,0 @@
{
"extends": [
"config:base"
],
"postUpdateOptions": [
"gomodTidy"
]
}

View File

@ -56,3 +56,34 @@ func PktInfo6(info *Inet6Pktinfo) []byte {
*(*Inet6Pktinfo)(h.data(0)) = *info *(*Inet6Pktinfo)(h.data(0)) = *info
return b 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
}
}

View File

@ -641,13 +641,13 @@ type Eproc struct {
Tdev int32 Tdev int32
Tpgid int32 Tpgid int32
Tsess uintptr Tsess uintptr
Wmesg [8]int8 Wmesg [8]byte
Xsize int32 Xsize int32
Xrssize int16 Xrssize int16
Xccount int16 Xccount int16
Xswrss int16 Xswrss int16
Flag int32 Flag int32
Login [12]int8 Login [12]byte
Spare [4]int32 Spare [4]int32
_ [4]byte _ [4]byte
} }
@ -688,7 +688,7 @@ type ExternProc struct {
P_priority uint8 P_priority uint8
P_usrpri uint8 P_usrpri uint8
P_nice int8 P_nice int8
P_comm [17]int8 P_comm [17]byte
P_pgrp uintptr P_pgrp uintptr
P_addr uintptr P_addr uintptr
P_xstat uint16 P_xstat uint16

View File

@ -641,13 +641,13 @@ type Eproc struct {
Tdev int32 Tdev int32
Tpgid int32 Tpgid int32
Tsess uintptr Tsess uintptr
Wmesg [8]int8 Wmesg [8]byte
Xsize int32 Xsize int32
Xrssize int16 Xrssize int16
Xccount int16 Xccount int16
Xswrss int16 Xswrss int16
Flag int32 Flag int32
Login [12]int8 Login [12]byte
Spare [4]int32 Spare [4]int32
_ [4]byte _ [4]byte
} }
@ -688,7 +688,7 @@ type ExternProc struct {
P_priority uint8 P_priority uint8
P_usrpri uint8 P_usrpri uint8
P_nice int8 P_nice int8
P_comm [17]int8 P_comm [17]byte
P_pgrp uintptr P_pgrp uintptr
P_addr uintptr P_addr uintptr
P_xstat uint16 P_xstat uint16

View File

@ -17,8 +17,6 @@ const (
SC_MANAGER_ALL_ACCESS = 0xf003f SC_MANAGER_ALL_ACCESS = 0xf003f
) )
//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenSCManagerW
const ( const (
SERVICE_KERNEL_DRIVER = 1 SERVICE_KERNEL_DRIVER = 1
SERVICE_FILE_SYSTEM_DRIVER = 2 SERVICE_FILE_SYSTEM_DRIVER = 2
@ -133,6 +131,14 @@ const (
SC_EVENT_DATABASE_CHANGE = 0 SC_EVENT_DATABASE_CHANGE = 0
SC_EVENT_PROPERTY_CHANGE = 1 SC_EVENT_PROPERTY_CHANGE = 1
SC_EVENT_STATUS_CHANGE = 2 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 { type SERVICE_STATUS struct {
@ -217,6 +223,7 @@ type QUERY_SERVICE_LOCK_STATUS struct {
LockDuration uint32 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 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 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 //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 SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) = sechost.SubscribeServiceChangeNotifications?
//sys UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications? //sys UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications?
//sys RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) = advapi32.RegisterServiceCtrlHandlerExW //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?

View File

@ -115,6 +115,7 @@ var (
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W") procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W")
procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW") procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW")
procQueryServiceDynamicInformation = modadvapi32.NewProc("QueryServiceDynamicInformation")
procQueryServiceLockStatusW = modadvapi32.NewProc("QueryServiceLockStatusW") procQueryServiceLockStatusW = modadvapi32.NewProc("QueryServiceLockStatusW")
procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
procQueryServiceStatusEx = modadvapi32.NewProc("QueryServiceStatusEx") procQueryServiceStatusEx = modadvapi32.NewProc("QueryServiceStatusEx")
@ -366,9 +367,9 @@ var (
procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo") procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
procNtCreateFile = modntdll.NewProc("NtCreateFile") procNtCreateFile = modntdll.NewProc("NtCreateFile")
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile") procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
procNtQueryInformationProcess = modntdll.NewProc("NtQueryInformationProcess") procNtQueryInformationProcess = modntdll.NewProc("NtQueryInformationProcess")
procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation") procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation")
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
procNtSetInformationProcess = modntdll.NewProc("NtSetInformationProcess") procNtSetInformationProcess = modntdll.NewProc("NtSetInformationProcess")
procNtSetSystemInformation = modntdll.NewProc("NtSetSystemInformation") procNtSetSystemInformation = modntdll.NewProc("NtSetSystemInformation")
procRtlAddFunctionTable = modntdll.NewProc("RtlAddFunctionTable") procRtlAddFunctionTable = modntdll.NewProc("RtlAddFunctionTable")
@ -976,6 +977,18 @@ func QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, buf
return 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) { 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) r1, _, e1 := syscall.Syscall6(procQueryServiceLockStatusW.Addr(), 4, uintptr(mgr), uintptr(unsafe.Pointer(lockStatus)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0, 0)
if r1 == 0 { if r1 == 0 {
@ -3171,14 +3184,6 @@ func NtCreateNamedPipeFile(pipe *Handle, access uint32, oa *OBJECT_ATTRIBUTES, i
return 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) { 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) r0, _, _ := syscall.Syscall6(procNtQueryInformationProcess.Addr(), 5, uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), uintptr(unsafe.Pointer(retLen)), 0)
if r0 != 0 { if r0 != 0 {
@ -3195,6 +3200,14 @@ func NtQuerySystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInf
return 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) { 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) r0, _, _ := syscall.Syscall6(procNtSetInformationProcess.Addr(), 4, uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), 0, 0)
if r0 != 0 { if r0 != 0 {

15
vendor/modules.txt vendored
View File

@ -17,7 +17,6 @@ github.com/cloudfoundry/jibber_jabber
## explicit ## explicit
github.com/creack/pty github.com/creack/pty
# github.com/davecgh/go-spew v1.1.1 # github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew github.com/davecgh/go-spew/spew
# github.com/emirpasic/gods v1.12.0 # github.com/emirpasic/gods v1.12.0
github.com/emirpasic/gods/containers github.com/emirpasic/gods/containers
@ -105,7 +104,7 @@ github.com/gobwas/glob/util/strings
github.com/golang-collections/collections/stack github.com/golang-collections/collections/stack
# github.com/golang/protobuf v1.3.2 # github.com/golang/protobuf v1.3.2
## explicit ## explicit
# github.com/google/go-cmp v0.3.1 # github.com/google/go-cmp v0.5.6
## explicit ## explicit
# github.com/gookit/color v1.4.2 # github.com/gookit/color v1.4.2
## explicit ## explicit
@ -116,8 +115,6 @@ github.com/imdario/mergo
# github.com/integrii/flaggy v1.4.0 # github.com/integrii/flaggy v1.4.0
## explicit ## explicit
github.com/integrii/flaggy 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 v0.0.0-20150711004518-d14ea06fba99
github.com/jbenet/go-context/io github.com/jbenet/go-context/io
# github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 # 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/index
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder 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 ## explicit
github.com/jesseduffield/gocui github.com/jesseduffield/gocui
# github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e # 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 # github.com/lucasb-eyer/go-colorful v1.2.0
## explicit ## explicit
github.com/lucasb-eyer/go-colorful github.com/lucasb-eyer/go-colorful
# github.com/mattn/go-colorable v0.1.7 # github.com/mattn/go-colorable v0.1.11
## explicit ## explicit
github.com/mattn/go-colorable 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-isatty
# github.com/mattn/go-runewidth v0.0.13 # github.com/mattn/go-runewidth v0.0.13
## explicit ## explicit
@ -208,8 +205,6 @@ github.com/mitchellh/go-homedir
## explicit ## explicit
# github.com/onsi/gomega v1.7.1 # github.com/onsi/gomega v1.7.1
## explicit ## explicit
# github.com/ozeidan/fuzzy-patricia v3.0.0+incompatible
## explicit
# github.com/pmezard/go-difflib v1.0.0 # github.com/pmezard/go-difflib v1.0.0
github.com/pmezard/go-difflib/difflib github.com/pmezard/go-difflib/difflib
# github.com/rivo/uniseg v0.2.0 # 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/context
golang.org/x/net/internal/socks golang.org/x/net/internal/socks
golang.org/x/net/proxy golang.org/x/net/proxy
# golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 # golang.org/x/sys v0.0.0-20211031064116-611d5d643895
## explicit ## explicit
golang.org/x/sys/cpu golang.org/x/sys/cpu
golang.org/x/sys/internal/unsafeheader golang.org/x/sys/internal/unsafeheader