mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-28 02:07:09 -04:00
Bump github.com/gdamore/tcell/v3 from 3.4.1 to 3.4.2
Bumps [github.com/gdamore/tcell/v3](https://github.com/gdamore/tcell) from 3.4.1 to 3.4.2. - [Release notes](https://github.com/gdamore/tcell/releases) - [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv3.md) - [Commits](https://github.com/gdamore/tcell/compare/v3.4.1...v3.4.2) --- updated-dependencies: - dependency-name: github.com/gdamore/tcell/v3 dependency-version: 3.4.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
+6
@@ -14,6 +14,8 @@
|
||||
|
||||
package tcell
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
type cell struct {
|
||||
currStr string
|
||||
lastStr string
|
||||
@@ -76,6 +78,10 @@ func (cb *CellBuffer) put(x int, y int, str string, style Style) (string, int) {
|
||||
// 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 if len(str) > 0 && str[0] >= ' ' && str[0] <= '~' && (len(str) == 1 || str[1] < utf8.RuneSelf) {
|
||||
// Printable ASCII followed by ASCII cannot be part of a larger
|
||||
// grapheme cluster, so avoid constructing a grapheme iterator.
|
||||
cl, width, str = str[:1], 1, str[1:]
|
||||
} else {
|
||||
g := textWidthOptions.StringGraphemes(str)
|
||||
for width == 0 && g.Next() {
|
||||
|
||||
+87
-23
@@ -63,10 +63,22 @@ const (
|
||||
// before they can grow without bound while waiting for a string terminator.
|
||||
const defaultControlStringLimit = 64 * 1024
|
||||
|
||||
const (
|
||||
// loneEscapeTimeout keeps bare Escape responsive when using legacy
|
||||
// keyboard reporting, where ESC can also prefix an Alt-modified key.
|
||||
loneEscapeTimeout = 200 * time.Millisecond
|
||||
|
||||
// escapeSequenceTimeout bounds incomplete escape sequences. Once a
|
||||
// sequence introducer has arrived, it is no longer ambiguous with a lone
|
||||
// Escape and can tolerate a substantially longer inter-byte delay.
|
||||
escapeSequenceTimeout = time.Second
|
||||
)
|
||||
|
||||
func newInputParser(eq chan<- Event) *inputParser {
|
||||
return &inputParser{
|
||||
evch: eq,
|
||||
buf: make([]rune, 0, 128),
|
||||
legacy: true,
|
||||
controlStringMax: defaultControlStringLimit,
|
||||
}
|
||||
}
|
||||
@@ -91,6 +103,7 @@ type inputParser struct {
|
||||
nested *inputParser // for buggy win32-input-mode implementations
|
||||
surrogate rune // high surrogate pair seen (for Win32 input mode)
|
||||
advanced bool // use advanced key reporting semantics
|
||||
legacy bool // keyboard protocol has ambiguous ESC prefixes
|
||||
controlStringMax int // maximum inbound OSC/XDA payload size; 0 means unlimited
|
||||
discardString bool // drop the rest of an over-limit OSC/XDA sequence
|
||||
}
|
||||
@@ -129,6 +142,35 @@ func (ip *inputParser) Waiting() bool {
|
||||
return ip.state != istInit
|
||||
}
|
||||
|
||||
// waitDuration reports how long to wait for the next byte before resetting an
|
||||
// incomplete escape sequence. A bare ESC is only ambiguous with legacy
|
||||
// keyboard reporting; other protocols can use the longer sequence deadline.
|
||||
func (ip *inputParser) waitDuration() time.Duration {
|
||||
if ip.state == istInit {
|
||||
return 0
|
||||
}
|
||||
if ip.state == istEsc && ip.legacy {
|
||||
return loneEscapeTimeout
|
||||
}
|
||||
return escapeSequenceTimeout
|
||||
}
|
||||
|
||||
func (ip *inputParser) WaitDuration() time.Duration {
|
||||
ip.l.Lock()
|
||||
defer ip.l.Unlock()
|
||||
return ip.waitDuration()
|
||||
}
|
||||
|
||||
func (ip *inputParser) SetKeyboardProtocol(protocol KeyProtocol) {
|
||||
ip.l.Lock()
|
||||
ip.legacy = protocol == LegacyKeyboard
|
||||
nested := ip.nested
|
||||
ip.l.Unlock()
|
||||
if nested != nil {
|
||||
nested.SetKeyboardProtocol(protocol)
|
||||
}
|
||||
}
|
||||
|
||||
// SetPixelMouse toggles whether SGR mouse reports are interpreted as
|
||||
// pixel coordinates (CSI ?1016h) rather than character cells (CSI ?1006h).
|
||||
// When enabled, mouse coordinates are not clipped to the screen size.
|
||||
@@ -488,25 +530,46 @@ var winKeys = map[int]Key{
|
||||
0x87: KeyF24, // vkF24
|
||||
}
|
||||
|
||||
type ss3Key struct {
|
||||
key Key
|
||||
str string
|
||||
}
|
||||
|
||||
// keys by their SS3 - used in application mode usually (legacy VT-style)
|
||||
var ss3Keys = map[rune]Key{
|
||||
'A': KeyUp,
|
||||
'B': KeyDown,
|
||||
'C': KeyRight,
|
||||
'D': KeyLeft,
|
||||
'E': KeyClear,
|
||||
'F': KeyEnd,
|
||||
'H': KeyHome,
|
||||
'P': KeyF1,
|
||||
'Q': KeyF2,
|
||||
'R': KeyF3,
|
||||
'S': KeyF4,
|
||||
't': KeyF5,
|
||||
'u': KeyF6,
|
||||
'v': KeyF7,
|
||||
'l': KeyF8,
|
||||
'w': KeyF9,
|
||||
'x': KeyF10,
|
||||
var ss3Keys = map[rune]ss3Key{
|
||||
'A': {key: KeyUp},
|
||||
'B': {key: KeyDown},
|
||||
'C': {key: KeyRight},
|
||||
'D': {key: KeyLeft},
|
||||
'E': {key: KeyClear},
|
||||
'F': {key: KeyEnd},
|
||||
'H': {key: KeyHome},
|
||||
'P': {key: KeyF1},
|
||||
'Q': {key: KeyF2},
|
||||
'R': {key: KeyF3},
|
||||
'S': {key: KeyF4},
|
||||
|
||||
// DEC application-keypad sequences. The VT100 terminfo entry calls some
|
||||
// of these F5-F10, but that is a terminfo naming artifact: a VT100 has
|
||||
// only PF1-PF4. Decode them by their PC keypad navigation meanings.
|
||||
'p': {key: KeyInsert},
|
||||
'q': {key: KeyEnd},
|
||||
'r': {key: KeyDown},
|
||||
's': {key: KeyPgDn},
|
||||
't': {key: KeyLeft},
|
||||
'u': {key: KeyClear},
|
||||
'v': {key: KeyRight},
|
||||
'w': {key: KeyHome},
|
||||
'x': {key: KeyUp},
|
||||
'y': {key: KeyPgUp},
|
||||
'M': {key: KeyEnter},
|
||||
'n': {key: KeyDelete},
|
||||
'j': {key: KeyRune, str: "*"},
|
||||
'k': {key: KeyRune, str: "+"},
|
||||
'l': {key: KeyRune, str: ","},
|
||||
'm': {key: KeyRune, str: "-"},
|
||||
'o': {key: KeyRune, str: "/"},
|
||||
'X': {key: KeyRune, str: "="},
|
||||
}
|
||||
|
||||
// linux terminal uses these non ECMA keys prefixed by CSI-[
|
||||
@@ -669,16 +732,16 @@ func (ip *inputParser) scan() {
|
||||
// parameters that do not match one of these forms, we just discard it.
|
||||
if len(ip.csiParams) == 0 {
|
||||
// simple SS3 case
|
||||
ip.postKey(k, "", ModNone)
|
||||
ip.postKey(k.key, k.str, ModNone)
|
||||
} else if parts := strings.Split(string(ip.csiParams), ";"); len(parts) >= 1 {
|
||||
// SS3 with modifier (old style). Note old terminfo would declare these as high
|
||||
// numbered function keys, but we encode as modified since that's how they are entered.
|
||||
if len(parts) >= 2 {
|
||||
if m, err := strconv.Atoi(parts[1]); err == nil && (parts[0] == "1" || parts[0] == "") {
|
||||
ip.postKey(k, "", calcModifier(m))
|
||||
ip.postKey(k.key, k.str, calcModifier(m))
|
||||
}
|
||||
} else if m, err := strconv.Atoi(parts[0]); err == nil {
|
||||
ip.postKey(k, "", calcModifier(m))
|
||||
ip.postKey(k.key, k.str, calcModifier(m))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -757,7 +820,7 @@ func (ip *inputParser) scan() {
|
||||
}
|
||||
}
|
||||
|
||||
if ip.state != istInit && time.Since(ip.keyTime) > time.Millisecond*50 {
|
||||
if timeout := ip.waitDuration(); timeout > 0 && time.Since(ip.keyTime) > timeout {
|
||||
if ip.state == istEsc {
|
||||
ip.postKey(KeyEscape, "", ModNone)
|
||||
} else if ec := ip.escChar; ec != 0 {
|
||||
@@ -1056,6 +1119,7 @@ func (ip *inputParser) handleWinKey(P []int) {
|
||||
rows: ip.rows,
|
||||
cols: ip.cols,
|
||||
advanced: ip.advanced,
|
||||
legacy: ip.legacy,
|
||||
pixelMouse: ip.pixelMouse,
|
||||
controlStringMax: ip.controlStringMax,
|
||||
}
|
||||
@@ -1425,7 +1489,7 @@ func (ip *inputParser) handleCsi(mode rune, params []byte, intermediate []byte)
|
||||
|
||||
// this might have been an SS3 style key with modifiers applied
|
||||
if k, ok := ss3Keys[mode]; ok && P0 == 1 && len(P) > 1 {
|
||||
ip.postKeyEx(k, "", calcModifier(P[1]), pressed, 0, repeat)
|
||||
ip.postKeyEx(k.key, k.str, calcModifier(P[1]), pressed, 0, repeat)
|
||||
return
|
||||
}
|
||||
// if we got here we just swallow the unknown sequence
|
||||
|
||||
+64
-7
@@ -286,6 +286,11 @@ type tScreen struct {
|
||||
advancedKeys bool
|
||||
controlStringLimit int
|
||||
input *inputParser
|
||||
compat struct {
|
||||
mouseUnsupported bool
|
||||
focusUnsupported bool
|
||||
clipboardReadUnsupported bool
|
||||
}
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
@@ -780,6 +785,10 @@ func (t *tScreen) emitAttrs(attrs AttrMask) {
|
||||
// The assumption is that sgr0 was already printed ahead of this.
|
||||
func (t *tScreen) emitUnderline(us UnderlineStyle, uc Color) {
|
||||
if us != UnderlineStyleNone {
|
||||
if t.legacy {
|
||||
t.Print(underline)
|
||||
return
|
||||
}
|
||||
// NB: under color should have been reset by sgr0
|
||||
if uc.IsRGB() {
|
||||
r, g, b := uc.RGB()
|
||||
@@ -1100,6 +1109,9 @@ func (t *tScreen) enableMouse(f MouseFlags) {
|
||||
// so we enable the mouse unconditionally unless we get a report
|
||||
// that says we have mouse, but not SGR mouse. This is suboptimal, but
|
||||
// a concession forced by the sorry state of terminal emulators.
|
||||
if t.compat.mouseUnsupported {
|
||||
return
|
||||
}
|
||||
if t.mouseDisabled {
|
||||
f = 0
|
||||
}
|
||||
@@ -1203,10 +1215,16 @@ func (t *tScreen) DisableFocus() {
|
||||
}
|
||||
|
||||
func (t *tScreen) enableFocusReporting() {
|
||||
if t.compat.focusUnsupported {
|
||||
return
|
||||
}
|
||||
t.Print(vt.PmFocusReports.Enable())
|
||||
}
|
||||
|
||||
func (t *tScreen) disableFocusReporting() {
|
||||
if t.compat.focusUnsupported {
|
||||
return
|
||||
}
|
||||
t.Print(vt.PmFocusReports.Disable())
|
||||
}
|
||||
|
||||
@@ -1336,13 +1354,18 @@ func (t *tScreen) mainLoop(stopQ chan struct{}) {
|
||||
case chunk := <-t.keyQ:
|
||||
buf.Write(chunk)
|
||||
t.scanInput(buf)
|
||||
if t.input.Waiting() {
|
||||
ta = time.After(time.Millisecond * 100)
|
||||
if timeout := t.input.WaitDuration(); timeout > 0 {
|
||||
ta = time.After(timeout)
|
||||
} else {
|
||||
ta = nil
|
||||
}
|
||||
case <-ta:
|
||||
t.input.Scan()
|
||||
if timeout := t.input.WaitDuration(); timeout > 0 {
|
||||
ta = time.After(timeout)
|
||||
} else {
|
||||
ta = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1373,7 +1396,11 @@ func (t *tScreen) inputLoop(stopQ chan struct{}) {
|
||||
return
|
||||
}
|
||||
if n > 0 {
|
||||
t.keyQ <- chunk[:n]
|
||||
select {
|
||||
case t.keyQ <- chunk[:n]:
|
||||
case <-t.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1449,7 +1476,31 @@ func (t *tScreen) Tty() (Tty, bool) {
|
||||
return t.tty, true
|
||||
}
|
||||
|
||||
func (t *tScreen) applyKnownTerminalProfile(goos, termProgram string) bool {
|
||||
func isSTTerminal(term string) bool {
|
||||
return term == "st" || strings.HasPrefix(term, "st-")
|
||||
}
|
||||
|
||||
func (t *tScreen) applyKnownTerminalProfile(goos, term, termProgram string) bool {
|
||||
if isSTTerminal(term) {
|
||||
// st implements a small subset of xterm extensions. In particular,
|
||||
// it has neither an advanced keyboard protocol nor SGR mouse or focus
|
||||
// reporting. It also reports unsupported CSI and OSC sequences to
|
||||
// stderr, so avoid probing or using extensions it does not implement.
|
||||
t.legacy = true
|
||||
t.compat.mouseUnsupported = true
|
||||
t.compat.focusUnsupported = true
|
||||
t.enterUrl = ""
|
||||
t.exitUrl = ""
|
||||
t.setWinSize = ""
|
||||
t.saveTitle = ""
|
||||
t.restoreTitle = ""
|
||||
t.setTitle = "\x1b]2;%s\x1b\\"
|
||||
t.notifyDesktop = ""
|
||||
t.compat.clipboardReadUnsupported = true
|
||||
t.termName = "st"
|
||||
return true
|
||||
}
|
||||
|
||||
switch termProgram {
|
||||
case "Apple_Terminal":
|
||||
// macOS Terminal.app cannot handle the startup queries, but it does
|
||||
@@ -1525,7 +1576,7 @@ func (t *tScreen) engageLocked() error {
|
||||
// Eventually they'll hopefully fix this. As the environment variable
|
||||
// does not convey by default via ssh, remote sessions might see spurious characters
|
||||
// emitted during startup. See the blog post for alternatives.
|
||||
if !t.applyKnownTerminalProfile(runtime.GOOS, os.Getenv("TERM_PROGRAM")) && t.negotiate {
|
||||
if !t.applyKnownTerminalProfile(runtime.GOOS, t.term, os.Getenv("TERM_PROGRAM")) && t.negotiate {
|
||||
if useVTWindowSizeQuery(runtime.GOOS) {
|
||||
t.Print(requestWindowSize)
|
||||
}
|
||||
@@ -1551,6 +1602,7 @@ func (t *tScreen) engageLocked() error {
|
||||
}
|
||||
t.processInitQ()
|
||||
t.applyKeyboardProtocolOverride()
|
||||
t.input.SetKeyboardProtocol(t.keyboardProtocol())
|
||||
if t.useAltScreen() {
|
||||
// Technically this may not be right, but every terminal we know about
|
||||
// (even Wyse 60) uses this to enter the alternate screen buffer, and
|
||||
@@ -1589,7 +1641,7 @@ func (t *tScreen) engageLocked() error {
|
||||
if t.title != "" && t.setTitle != "" {
|
||||
t.Printf(t.setTitle, t.title)
|
||||
}
|
||||
if t.negotiate && useVTWindowSizeQuery(runtime.GOOS) {
|
||||
if t.negotiate && !t.legacy && useVTWindowSizeQuery(runtime.GOOS) {
|
||||
t.Print(requestWindowSize)
|
||||
}
|
||||
|
||||
@@ -1744,7 +1796,7 @@ func (t *tScreen) GetClipboard() {
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
if t.setClipboard != "" {
|
||||
if !t.compat.clipboardReadUnsupported && t.setClipboard != "" {
|
||||
t.Printf(t.setClipboard, "?")
|
||||
}
|
||||
t.Unlock()
|
||||
@@ -1773,6 +1825,11 @@ func (t *tScreen) Terminal() (string, string) {
|
||||
func (t *tScreen) KeyboardProtocol() KeyProtocol {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
return t.keyboardProtocol()
|
||||
}
|
||||
|
||||
// keyboardProtocol reports the selected keyboard protocol while t is locked.
|
||||
func (t *tScreen) keyboardProtocol() KeyProtocol {
|
||||
if t.haveWin32Kbd {
|
||||
return Win32Keyboard
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user