mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-17 07:43:39 -05:00
Add keybindings for CTRL, ALT, SHIFT + UP, DOWN, RIGHT, LEFT, HOME, END, BACKSPACE, DELETE & more (#3996)
* Added tests for `LightRenderer`
* Added common SHIFT, ALT and ALT+SHIFT key sequences
* Added common CTRL key sequences
* Added common CTRL+ALT, CTRL+SHIFT, CTRL+ALT+SHIFT key sequences
* Added proper xterm META modifier handling
according to defc6dd568/input.c (L357-L375)
* Fix `ctrl-backspace` and `ctrl-alt-backspace`
* Fix broken tcell tests on windows by swallowing Resize events
* Added tests for FullscreenRenderer
* Removed own fork of tcell and updated tcell to 2.9.0
tcell 2.9.0 is needed for `Ctrl-Alt-*` and `Ctrl-Alt-Shift-*` shortcuts in Windows
* Replace conditional checks with switch statements to improve readability
* Replace long conditionals with constant slices to improve readability
* Bind `ctrl-bspace` (`ctrl-h`) to `backward-delete-char` by default
Since we now distinguish between Backspace and Ctrl-Backspace, Ctrl-Backspace should trigger the same action as Backspace by default. In that way nothing changes for the user but you can bind other actions to Ctrl-Backspace when desired.
This commit is contained in:
284
src/tui/light.go
284
src/tui/light.go
@@ -335,6 +335,8 @@ func (r *LightRenderer) GetChar() Event {
|
||||
return Event{CtrlQ, 0, nil}
|
||||
case 127:
|
||||
return Event{Backspace, 0, nil}
|
||||
case 8:
|
||||
return Event{CtrlBackspace, 0, nil}
|
||||
case 0:
|
||||
return Event{CtrlSpace, 0, nil}
|
||||
case 28:
|
||||
@@ -381,6 +383,9 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
||||
}
|
||||
|
||||
*sz = 2
|
||||
if r.buffer[1] == 8 {
|
||||
return Event{CtrlAltBackspace, 0, nil}
|
||||
}
|
||||
if r.buffer[1] >= 1 && r.buffer[1] <= 'z'-'a'+1 {
|
||||
return CtrlAltKey(rune(r.buffer[1] + 'a' - 1))
|
||||
}
|
||||
@@ -473,22 +478,136 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
||||
if r.buffer[3] == '~' {
|
||||
return Event{Delete, 0, nil}
|
||||
}
|
||||
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
|
||||
switch r.buffer[5] {
|
||||
case '0':
|
||||
return Event{AltShiftDelete, 0, nil}
|
||||
case '1':
|
||||
return Event{AltDelete, 0, nil}
|
||||
case '2':
|
||||
return Event{AltShiftDelete, 0, nil}
|
||||
case '3':
|
||||
return Event{CtrlAltDelete, 0, nil}
|
||||
case '4':
|
||||
return Event{CtrlAltShiftDelete, 0, nil}
|
||||
case '5':
|
||||
return Event{CtrlAltDelete, 0, nil}
|
||||
case '6':
|
||||
return Event{CtrlAltShiftDelete, 0, nil}
|
||||
}
|
||||
}
|
||||
if len(r.buffer) == 6 && r.buffer[5] == '~' {
|
||||
*sz = 6
|
||||
switch r.buffer[4] {
|
||||
case '5':
|
||||
return Event{CtrlDelete, 0, nil}
|
||||
case '2':
|
||||
return Event{ShiftDelete, 0, nil}
|
||||
case '3':
|
||||
return Event{AltDelete, 0, nil}
|
||||
case '4':
|
||||
return Event{AltShiftDelete, 0, nil}
|
||||
case '5':
|
||||
return Event{CtrlDelete, 0, nil}
|
||||
case '6':
|
||||
return Event{CtrlShiftDelete, 0, nil}
|
||||
case '7':
|
||||
return Event{CtrlAltDelete, 0, nil}
|
||||
case '8':
|
||||
return Event{CtrlAltShiftDelete, 0, nil}
|
||||
case '9':
|
||||
return Event{AltDelete, 0, nil}
|
||||
}
|
||||
}
|
||||
return Event{Invalid, 0, nil}
|
||||
case '4':
|
||||
return Event{End, 0, nil}
|
||||
case '5':
|
||||
return Event{PageUp, 0, nil}
|
||||
if r.buffer[3] == '~' {
|
||||
return Event{PageUp, 0, nil}
|
||||
}
|
||||
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
|
||||
switch r.buffer[5] {
|
||||
case '0':
|
||||
return Event{AltShiftPageUp, 0, nil}
|
||||
case '1':
|
||||
return Event{AltPageUp, 0, nil}
|
||||
case '2':
|
||||
return Event{AltShiftPageUp, 0, nil}
|
||||
case '3':
|
||||
return Event{CtrlAltPageUp, 0, nil}
|
||||
case '4':
|
||||
return Event{CtrlAltShiftPageUp, 0, nil}
|
||||
case '5':
|
||||
return Event{CtrlAltPageUp, 0, nil}
|
||||
case '6':
|
||||
return Event{CtrlAltShiftPageUp, 0, nil}
|
||||
}
|
||||
}
|
||||
if len(r.buffer) == 6 && r.buffer[5] == '~' {
|
||||
*sz = 6
|
||||
switch r.buffer[4] {
|
||||
case '2':
|
||||
return Event{ShiftPageUp, 0, nil}
|
||||
case '3':
|
||||
return Event{AltPageUp, 0, nil}
|
||||
case '4':
|
||||
return Event{AltShiftPageUp, 0, nil}
|
||||
case '5':
|
||||
return Event{CtrlPageUp, 0, nil}
|
||||
case '6':
|
||||
return Event{CtrlShiftPageUp, 0, nil}
|
||||
case '7':
|
||||
return Event{CtrlAltPageUp, 0, nil}
|
||||
case '8':
|
||||
return Event{CtrlAltShiftPageUp, 0, nil}
|
||||
case '9':
|
||||
return Event{AltPageUp, 0, nil}
|
||||
}
|
||||
}
|
||||
return Event{Invalid, 0, nil}
|
||||
case '6':
|
||||
return Event{PageDown, 0, nil}
|
||||
if r.buffer[3] == '~' {
|
||||
return Event{PageDown, 0, nil}
|
||||
}
|
||||
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
|
||||
switch r.buffer[5] {
|
||||
case '0':
|
||||
return Event{AltShiftPageDown, 0, nil}
|
||||
case '1':
|
||||
return Event{AltPageDown, 0, nil}
|
||||
case '2':
|
||||
return Event{AltShiftPageDown, 0, nil}
|
||||
case '3':
|
||||
return Event{CtrlAltPageDown, 0, nil}
|
||||
case '4':
|
||||
return Event{CtrlAltShiftPageDown, 0, nil}
|
||||
case '5':
|
||||
return Event{CtrlAltPageDown, 0, nil}
|
||||
case '6':
|
||||
return Event{CtrlAltShiftPageDown, 0, nil}
|
||||
}
|
||||
}
|
||||
if len(r.buffer) == 6 && r.buffer[5] == '~' {
|
||||
*sz = 6
|
||||
switch r.buffer[4] {
|
||||
case '2':
|
||||
return Event{ShiftPageDown, 0, nil}
|
||||
case '3':
|
||||
return Event{AltPageDown, 0, nil}
|
||||
case '4':
|
||||
return Event{AltShiftPageDown, 0, nil}
|
||||
case '5':
|
||||
return Event{CtrlPageDown, 0, nil}
|
||||
case '6':
|
||||
return Event{CtrlShiftPageDown, 0, nil}
|
||||
case '7':
|
||||
return Event{CtrlAltPageDown, 0, nil}
|
||||
case '8':
|
||||
return Event{CtrlAltShiftPageDown, 0, nil}
|
||||
case '9':
|
||||
return Event{AltPageDown, 0, nil}
|
||||
}
|
||||
}
|
||||
return Event{Invalid, 0, nil}
|
||||
case '7':
|
||||
return Event{Home, 0, nil}
|
||||
case '8':
|
||||
@@ -526,63 +645,172 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
||||
}
|
||||
*sz = 6
|
||||
switch r.buffer[4] {
|
||||
case '1', '2', '3', '4', '5':
|
||||
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
// Kitty iTerm2 WezTerm
|
||||
// SHIFT-ARROW "\e[1;2D"
|
||||
// ALT-SHIFT-ARROW "\e[1;4D" "\e[1;10D" "\e[1;4D"
|
||||
// CTRL-SHIFT-ARROW "\e[1;6D" N/A
|
||||
// CMD-SHIFT-ARROW "\e[1;10D" N/A N/A ("\e[1;2D")
|
||||
alt := r.buffer[4] == '3'
|
||||
ctrl := bytes.IndexByte([]byte{'5', '6', '7', '8'}, r.buffer[4]) >= 0
|
||||
alt := bytes.IndexByte([]byte{'3', '4', '7', '8'}, r.buffer[4]) >= 0
|
||||
shift := bytes.IndexByte([]byte{'2', '4', '6', '8'}, r.buffer[4]) >= 0
|
||||
char := r.buffer[5]
|
||||
altShift := false
|
||||
if r.buffer[4] == '1' && r.buffer[5] == '0' {
|
||||
altShift = true
|
||||
if len(r.buffer) < 7 {
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
*sz = 7
|
||||
char = r.buffer[6]
|
||||
} else if r.buffer[4] == '4' {
|
||||
altShift = true
|
||||
if r.buffer[4] == '9' {
|
||||
ctrl = false
|
||||
alt = true
|
||||
shift = false
|
||||
if len(r.buffer) < 6 {
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
*sz = 6
|
||||
char = r.buffer[5]
|
||||
} else if r.buffer[4] == '1' && bytes.IndexByte([]byte{'0', '1', '2', '3', '4', '5', '6'}, r.buffer[5]) >= 0 {
|
||||
ctrl = bytes.IndexByte([]byte{'3', '4', '5', '6'}, r.buffer[5]) >= 0
|
||||
alt = true
|
||||
shift = bytes.IndexByte([]byte{'0', '2', '4', '6'}, r.buffer[5]) >= 0
|
||||
if len(r.buffer) < 7 {
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
*sz = 7
|
||||
char = r.buffer[6]
|
||||
}
|
||||
ctrlShift := ctrl && shift
|
||||
ctrlAlt := ctrl && alt
|
||||
altShift := alt && shift
|
||||
ctrlAltShift := ctrl && alt && shift
|
||||
switch char {
|
||||
case 'A':
|
||||
if alt {
|
||||
return Event{AltUp, 0, nil}
|
||||
if ctrlAltShift {
|
||||
return Event{CtrlAltShiftUp, 0, nil}
|
||||
}
|
||||
if ctrlAlt {
|
||||
return Event{CtrlAltUp, 0, nil}
|
||||
}
|
||||
if ctrlShift {
|
||||
return Event{CtrlShiftUp, 0, nil}
|
||||
}
|
||||
if altShift {
|
||||
return Event{AltShiftUp, 0, nil}
|
||||
}
|
||||
return Event{ShiftUp, 0, nil}
|
||||
case 'B':
|
||||
if ctrl {
|
||||
return Event{CtrlUp, 0, nil}
|
||||
}
|
||||
if alt {
|
||||
return Event{AltDown, 0, nil}
|
||||
return Event{AltUp, 0, nil}
|
||||
}
|
||||
if shift {
|
||||
return Event{ShiftUp, 0, nil}
|
||||
}
|
||||
case 'B':
|
||||
if ctrlAltShift {
|
||||
return Event{CtrlAltShiftDown, 0, nil}
|
||||
}
|
||||
if ctrlAlt {
|
||||
return Event{CtrlAltDown, 0, nil}
|
||||
}
|
||||
if ctrlShift {
|
||||
return Event{CtrlShiftDown, 0, nil}
|
||||
}
|
||||
if altShift {
|
||||
return Event{AltShiftDown, 0, nil}
|
||||
}
|
||||
return Event{ShiftDown, 0, nil}
|
||||
case 'C':
|
||||
if ctrl {
|
||||
return Event{CtrlDown, 0, nil}
|
||||
}
|
||||
if alt {
|
||||
return Event{AltRight, 0, nil}
|
||||
return Event{AltDown, 0, nil}
|
||||
}
|
||||
if shift {
|
||||
return Event{ShiftDown, 0, nil}
|
||||
}
|
||||
case 'C':
|
||||
if ctrlAltShift {
|
||||
return Event{CtrlAltShiftRight, 0, nil}
|
||||
}
|
||||
if ctrlAlt {
|
||||
return Event{CtrlAltRight, 0, nil}
|
||||
}
|
||||
if ctrlShift {
|
||||
return Event{CtrlShiftRight, 0, nil}
|
||||
}
|
||||
if altShift {
|
||||
return Event{AltShiftRight, 0, nil}
|
||||
}
|
||||
return Event{ShiftRight, 0, nil}
|
||||
case 'D':
|
||||
if ctrl {
|
||||
return Event{CtrlRight, 0, nil}
|
||||
}
|
||||
if shift {
|
||||
return Event{ShiftRight, 0, nil}
|
||||
}
|
||||
if alt {
|
||||
return Event{AltLeft, 0, nil}
|
||||
return Event{AltRight, 0, nil}
|
||||
}
|
||||
case 'D':
|
||||
if ctrlAltShift {
|
||||
return Event{CtrlAltShiftLeft, 0, nil}
|
||||
}
|
||||
if ctrlAlt {
|
||||
return Event{CtrlAltLeft, 0, nil}
|
||||
}
|
||||
if ctrlShift {
|
||||
return Event{CtrlShiftLeft, 0, nil}
|
||||
}
|
||||
if altShift {
|
||||
return Event{AltShiftLeft, 0, nil}
|
||||
}
|
||||
return Event{ShiftLeft, 0, nil}
|
||||
if ctrl {
|
||||
return Event{CtrlLeft, 0, nil}
|
||||
}
|
||||
if alt {
|
||||
return Event{AltLeft, 0, nil}
|
||||
}
|
||||
if shift {
|
||||
return Event{ShiftLeft, 0, nil}
|
||||
}
|
||||
case 'H':
|
||||
if ctrlAltShift {
|
||||
return Event{CtrlAltShiftHome, 0, nil}
|
||||
}
|
||||
if ctrlAlt {
|
||||
return Event{CtrlAltHome, 0, nil}
|
||||
}
|
||||
if ctrlShift {
|
||||
return Event{CtrlShiftHome, 0, nil}
|
||||
}
|
||||
if altShift {
|
||||
return Event{AltShiftHome, 0, nil}
|
||||
}
|
||||
if ctrl {
|
||||
return Event{CtrlHome, 0, nil}
|
||||
}
|
||||
if alt {
|
||||
return Event{AltHome, 0, nil}
|
||||
}
|
||||
if shift {
|
||||
return Event{ShiftHome, 0, nil}
|
||||
}
|
||||
case 'F':
|
||||
if ctrlAltShift {
|
||||
return Event{CtrlAltShiftEnd, 0, nil}
|
||||
}
|
||||
if ctrlAlt {
|
||||
return Event{CtrlAltEnd, 0, nil}
|
||||
}
|
||||
if ctrlShift {
|
||||
return Event{CtrlShiftEnd, 0, nil}
|
||||
}
|
||||
if altShift {
|
||||
return Event{AltShiftEnd, 0, nil}
|
||||
}
|
||||
if ctrl {
|
||||
return Event{CtrlEnd, 0, nil}
|
||||
}
|
||||
if alt {
|
||||
return Event{AltEnd, 0, nil}
|
||||
}
|
||||
if shift {
|
||||
return Event{ShiftEnd, 0, nil}
|
||||
}
|
||||
}
|
||||
} // r.buffer[4]
|
||||
} // r.buffer[3]
|
||||
|
||||
Reference in New Issue
Block a user