mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Bump tcell to an unreleased snapshot to fix a shutdown race
tcell's filterEvents goroutine sends events into eventQ with a plain
blocking send, while Fini (via finish/finalize) closes eventQ after
closing the quit channel. The goroutine can have already committed to
the ev = <-inQ select arm when quit is closed, so its send into eventQ
races with the close; the race detector flags this (send and close on
the same channel are unsynchronized), and if the close wins, the send
panics with "send on closed channel".
This was caught by the integration tests under the race detector,
where every test drives a real tScreen over a MockTerm and tears it
down via Fini, but it equally affects real-terminal shutdown.
Upstream fixed it in 243630d2 ("Fix screen Init/Fini races") by
tracking the filter goroutine in a WaitGroup that finalize waits for
before closing eventQ, and guarding the send with a select on quit.
That commit is not in a tagged release yet (latest is v3.4.0), so pin
the pseudo-version; the delta over v3.4.0 is just this fix, a Windows
key-release fix, a cell-rendering perf tweak, and dependency bumps.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
07745afc57
commit
14d717d77f
10
go.mod
10
go.mod
|
|
@ -13,7 +13,7 @@ require (
|
|||
github.com/cli/go-gh/v2 v2.13.0
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/gdamore/tcell/v3 v3.4.0
|
||||
github.com/gdamore/tcell/v3 v3.4.1-0.20260703153331-243630d2fb59
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.6.1
|
||||
github.com/integrii/flaggy v1.8.0
|
||||
|
|
@ -65,11 +65,11 @@ require (
|
|||
github.com/onsi/gomega v1.34.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/mod v0.36.0 // indirect
|
||||
golang.org/x/net v0.55.0 // indirect
|
||||
golang.org/x/term v0.43.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
golang.org/x/term v0.44.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
golang.org/x/tools v0.45.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
|
|
|
|||
20
go.sum
20
go.sum
|
|
@ -32,8 +32,8 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
|||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
|
||||
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
|
||||
github.com/gdamore/tcell/v3 v3.4.0 h1:VUym1HQZiYodA5PGQrqLxF7QwqQndcAUwQD7G7XUy5E=
|
||||
github.com/gdamore/tcell/v3 v3.4.0/go.mod h1:fjKxNiIFwbzTxDU+i+AAMz+xPOgXVaZq5tbShsKseHc=
|
||||
github.com/gdamore/tcell/v3 v3.4.1-0.20260703153331-243630d2fb59 h1:kUXexBZYoVdAJIOIuP6uLgK3k0G7ClDIRO27Z3epgtU=
|
||||
github.com/gdamore/tcell/v3 v3.4.1-0.20260703153331-243630d2fb59/go.mod h1:Ev/2PFhL0QtVmu6XPZG9NEuITAZ6XH7i7/BF3wupBdw=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
|
|
@ -139,8 +139,8 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0
|
|||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
|
|
@ -168,21 +168,21 @@ golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
|||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
|
|
|||
18
vendor/github.com/gdamore/tcell/v3/cell.go
generated
vendored
18
vendor/github.com/gdamore/tcell/v3/cell.go
generated
vendored
|
|
@ -72,12 +72,18 @@ func (cb *CellBuffer) put(x int, y int, str string, style Style) (string, int) {
|
|||
if x >= 0 && y >= 0 && x < cb.w && y < cb.h {
|
||||
var cl string
|
||||
c := &cb.cells[(y*cb.w)+x]
|
||||
g := textWidthOptions.StringGraphemes(str)
|
||||
for width == 0 && g.Next() {
|
||||
cluster := g.Value()
|
||||
cl += cluster
|
||||
width = g.Width()
|
||||
str = str[len(cluster):]
|
||||
if str == c.currStr && c.width > 0 {
|
||||
// Identical re-Put (a full-screen redraw): the grapheme split is
|
||||
// unchanged, so reuse the measured width instead of segmenting.
|
||||
cl, width, str = str, c.width, ""
|
||||
} else {
|
||||
g := textWidthOptions.StringGraphemes(str)
|
||||
for width == 0 && g.Next() {
|
||||
cluster := g.Value()
|
||||
cl += cluster
|
||||
width = g.Width()
|
||||
str = str[len(cluster):]
|
||||
}
|
||||
}
|
||||
|
||||
// Wide characters: we want to mark the "wide" cells
|
||||
|
|
|
|||
28
vendor/github.com/gdamore/tcell/v3/tscreen.go
generated
vendored
28
vendor/github.com/gdamore/tcell/v3/tscreen.go
generated
vendored
|
|
@ -243,6 +243,7 @@ type tScreen struct {
|
|||
legacy bool
|
||||
hasClipboard bool // true if OSC 52 reported via DA1
|
||||
finiOnce sync.Once
|
||||
initFiniLock sync.Mutex
|
||||
enterUrl string
|
||||
exitUrl string
|
||||
setWinSize string
|
||||
|
|
@ -258,6 +259,7 @@ type tScreen struct {
|
|||
running bool
|
||||
startTime time.Time
|
||||
wg sync.WaitGroup
|
||||
eventWg sync.WaitGroup
|
||||
mouseFlags MouseFlags
|
||||
pasteEnabled bool
|
||||
focusEnabled bool
|
||||
|
|
@ -355,6 +357,20 @@ func (t *tScreen) applyEnvironmentOverrides() {
|
|||
}
|
||||
|
||||
func (t *tScreen) Init() error {
|
||||
t.initFiniLock.Lock()
|
||||
defer t.initFiniLock.Unlock()
|
||||
|
||||
t.Lock()
|
||||
if t.fini {
|
||||
t.Unlock()
|
||||
return errors.New("screen finalized")
|
||||
}
|
||||
if t.running {
|
||||
t.Unlock()
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
t.Unlock()
|
||||
|
||||
if e := t.initialize(); e != nil {
|
||||
return e
|
||||
}
|
||||
|
|
@ -525,7 +541,9 @@ func (t *tScreen) processInitQ() {
|
|||
|
||||
func (t *tScreen) filterEvents() chan Event {
|
||||
inQ := make(chan Event, 128)
|
||||
t.eventWg.Add(1)
|
||||
go func() {
|
||||
defer t.eventWg.Done()
|
||||
for {
|
||||
var ev Event
|
||||
select {
|
||||
|
|
@ -541,7 +559,11 @@ func (t *tScreen) filterEvents() chan Event {
|
|||
}
|
||||
|
||||
default:
|
||||
t.eventQ <- ev
|
||||
select {
|
||||
case t.eventQ <- ev:
|
||||
case <-t.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
@ -596,6 +618,9 @@ func (t *tScreen) prepareCursorStyles() {
|
|||
}
|
||||
|
||||
func (t *tScreen) Fini() {
|
||||
t.initFiniLock.Lock()
|
||||
defer t.initFiniLock.Unlock()
|
||||
|
||||
// Ensure that enough time passes for terminals to finish sending
|
||||
// their initial response (gnome-terminal sends terminal dimensions
|
||||
// asynchronously later than the response to primary DA for some reason.)
|
||||
|
|
@ -1659,6 +1684,7 @@ func (t *tScreen) Beep() error {
|
|||
func (t *tScreen) finalize() {
|
||||
t.disengage()
|
||||
_ = t.tty.Close()
|
||||
t.eventWg.Wait()
|
||||
close(t.eventQ)
|
||||
}
|
||||
|
||||
|
|
|
|||
49
vendor/github.com/gdamore/tcell/v3/tty/tty_win.go
generated
vendored
49
vendor/github.com/gdamore/tcell/v3/tty/tty_win.go
generated
vendored
|
|
@ -20,6 +20,7 @@ package tty
|
|||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
|
@ -98,6 +99,38 @@ type inputRecord struct {
|
|||
data [16]byte
|
||||
}
|
||||
|
||||
func encodeWinKeyRecord(data [16]byte, surrogate *rune) []byte {
|
||||
keyDown := binary.LittleEndian.Uint32(data[0:]) != 0
|
||||
repeat := binary.LittleEndian.Uint16(data[4:])
|
||||
virtualKey := binary.LittleEndian.Uint16(data[6:])
|
||||
scanCode := binary.LittleEndian.Uint16(data[8:])
|
||||
// we normally only expect to see ascii, but paste data may come in as UTF-16.
|
||||
wc := rune(binary.LittleEndian.Uint16(data[10:]))
|
||||
controlState := binary.LittleEndian.Uint32(data[12:])
|
||||
|
||||
if virtualKey != 0 || scanCode != 0 {
|
||||
kd := 0
|
||||
if keyDown {
|
||||
kd = 1
|
||||
}
|
||||
return fmt.Appendf(nil, "\x1b[%d;%d;%d;%d;%d;%d_",
|
||||
virtualKey, scanCode, wc, kd, controlState, max(1, repeat))
|
||||
}
|
||||
|
||||
if !keyDown {
|
||||
return nil
|
||||
}
|
||||
|
||||
var encoded []byte
|
||||
decodedRunes := decodeUTF16Rune(surrogate, wc)
|
||||
for range max(1, repeat) {
|
||||
for _, decoded := range decodedRunes {
|
||||
encoded = append(encoded, []byte(string(decoded))...)
|
||||
}
|
||||
}
|
||||
return encoded
|
||||
}
|
||||
|
||||
type winTty struct {
|
||||
buf chan byte
|
||||
out syscall.Handle
|
||||
|
|
@ -207,17 +240,11 @@ func (w *winTty) getConsoleInput() error {
|
|||
ir := rec[i]
|
||||
switch ir.typ {
|
||||
case keyEvent:
|
||||
// we normally only expect to see ascii, but paste data may come in as UTF-16.
|
||||
wc := rune(binary.LittleEndian.Uint16(ir.data[10:]))
|
||||
for _, decoded := range decodeUTF16Rune(&w.surrogate, wc) {
|
||||
for _, chr := range []byte(string(decoded)) {
|
||||
// We normally expect only to see ASCII (win32-input-mode),
|
||||
// but apparently pasted data can arrive in UTF-16 here.
|
||||
select {
|
||||
case w.buf <- chr:
|
||||
case <-w.stopQ:
|
||||
break loop
|
||||
}
|
||||
for _, chr := range encodeWinKeyRecord(ir.data, &w.surrogate) {
|
||||
select {
|
||||
case w.buf <- chr:
|
||||
case <-w.stopQ:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
10
vendor/modules.txt
vendored
10
vendor/modules.txt
vendored
|
|
@ -48,7 +48,7 @@ github.com/fatih/color
|
|||
# github.com/gdamore/encoding v1.0.1
|
||||
## explicit; go 1.9
|
||||
github.com/gdamore/encoding
|
||||
# github.com/gdamore/tcell/v3 v3.4.0
|
||||
# github.com/gdamore/tcell/v3 v3.4.1-0.20260703153331-243630d2fb59
|
||||
## explicit; go 1.25.0
|
||||
github.com/gdamore/tcell/v3
|
||||
github.com/gdamore/tcell/v3/color
|
||||
|
|
@ -175,7 +175,7 @@ github.com/xo/terminfo
|
|||
## explicit; go 1.20
|
||||
golang.org/x/exp/constraints
|
||||
golang.org/x/exp/slices
|
||||
# golang.org/x/mod v0.35.0
|
||||
# golang.org/x/mod v0.36.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/mod/internal/lazyregexp
|
||||
golang.org/x/mod/modfile
|
||||
|
|
@ -192,10 +192,10 @@ golang.org/x/sync/semaphore
|
|||
golang.org/x/sys/plan9
|
||||
golang.org/x/sys/unix
|
||||
golang.org/x/sys/windows
|
||||
# golang.org/x/term v0.43.0
|
||||
# golang.org/x/term v0.44.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/term
|
||||
# golang.org/x/text v0.37.0
|
||||
# golang.org/x/text v0.38.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/text/cases
|
||||
golang.org/x/text/encoding
|
||||
|
|
@ -208,7 +208,7 @@ golang.org/x/text/language
|
|||
golang.org/x/text/runes
|
||||
golang.org/x/text/transform
|
||||
golang.org/x/text/unicode/norm
|
||||
# golang.org/x/tools v0.44.0
|
||||
# golang.org/x/tools v0.45.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/tools/go/ast/astutil
|
||||
# gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
|
||||
|
|
|
|||
Loading…
Reference in a new issue