mirror of
https://github.com/taigrr/bubbletea.git
synced 2026-04-02 02:59:09 -07:00
feat(keys): add support for shift/ctrl + arrow keys (#292)
* feat(keys): add support for shift/ctrl+arrow keys * chore(keys): use sequences for shift/ctrl arrow keys * feat(keys): support ctrl+shift+arrow keys * chore(keys): use sequences for alt+arrows * feat(keys): add support for arrow key combinations with alt modifiers * fix(keys): remove an extreaneous check * feat(keys): add support for urxvt arrow keys with modfiers * feat(keys): add support for arrow keys in DECCKM mode * docs(keys): expand on comment about ctrl+backtick * chore(keys): migrate various bindings to sequences * Minor comment improvements to the input parser Co-authored-by: Bwahharharrr <yitang@tutanota.com>
This commit is contained in:
203
key.go
203
key.go
@@ -199,6 +199,18 @@ const (
|
||||
KeyPgDown
|
||||
KeyDelete
|
||||
KeySpace
|
||||
KeyCtrlUp
|
||||
KeyCtrlDown
|
||||
KeyCtrlRight
|
||||
KeyCtrlLeft
|
||||
KeyShiftUp
|
||||
KeyShiftDown
|
||||
KeyShiftRight
|
||||
KeyShiftLeft
|
||||
KeyCtrlShiftUp
|
||||
KeyCtrlShiftDown
|
||||
KeyCtrlShiftLeft
|
||||
KeyCtrlShiftRight
|
||||
KeyF1
|
||||
KeyF2
|
||||
KeyF3
|
||||
@@ -224,7 +236,7 @@ const (
|
||||
// Mappings for control keys and other special keys to friendly consts.
|
||||
var keyNames = map[KeyType]string{
|
||||
// Control keys.
|
||||
keyNUL: "ctrl+@", // also ctrl+`
|
||||
keyNUL: "ctrl+@", // also ctrl+` (that's ctrl+backtick)
|
||||
keySOH: "ctrl+a",
|
||||
keySTX: "ctrl+b",
|
||||
keyETX: "ctrl+c",
|
||||
@@ -259,46 +271,127 @@ var keyNames = map[KeyType]string{
|
||||
keyDEL: "backspace",
|
||||
|
||||
// Other keys.
|
||||
KeyRunes: "runes",
|
||||
KeyUp: "up",
|
||||
KeyDown: "down",
|
||||
KeyRight: "right",
|
||||
KeySpace: " ", // for backwards compatibility
|
||||
KeyLeft: "left",
|
||||
KeyShiftTab: "shift+tab",
|
||||
KeyHome: "home",
|
||||
KeyEnd: "end",
|
||||
KeyPgUp: "pgup",
|
||||
KeyPgDown: "pgdown",
|
||||
KeyDelete: "delete",
|
||||
KeyF1: "f1",
|
||||
KeyF2: "f2",
|
||||
KeyF3: "f3",
|
||||
KeyF4: "f4",
|
||||
KeyF5: "f5",
|
||||
KeyF6: "f6",
|
||||
KeyF7: "f7",
|
||||
KeyF8: "f8",
|
||||
KeyF9: "f9",
|
||||
KeyF10: "f10",
|
||||
KeyF11: "f11",
|
||||
KeyF12: "f12",
|
||||
KeyF13: "f13",
|
||||
KeyF14: "f14",
|
||||
KeyF15: "f15",
|
||||
KeyF16: "f16",
|
||||
KeyF17: "f17",
|
||||
KeyF18: "f18",
|
||||
KeyF19: "f19",
|
||||
KeyF20: "f20",
|
||||
KeyRunes: "runes",
|
||||
KeyUp: "up",
|
||||
KeyDown: "down",
|
||||
KeyRight: "right",
|
||||
KeySpace: " ", // for backwards compatibility
|
||||
KeyLeft: "left",
|
||||
KeyShiftTab: "shift+tab",
|
||||
KeyHome: "home",
|
||||
KeyEnd: "end",
|
||||
KeyPgUp: "pgup",
|
||||
KeyPgDown: "pgdown",
|
||||
KeyDelete: "delete",
|
||||
KeyCtrlUp: "ctrl+up",
|
||||
KeyCtrlDown: "ctrl+down",
|
||||
KeyCtrlRight: "ctrl+right",
|
||||
KeyCtrlLeft: "ctrl+left",
|
||||
KeyShiftUp: "shift+up",
|
||||
KeyShiftDown: "shift+down",
|
||||
KeyShiftRight: "shift+right",
|
||||
KeyShiftLeft: "shift+left",
|
||||
KeyCtrlShiftUp: "ctrl+shift+up",
|
||||
KeyCtrlShiftDown: "ctrl+shift+down",
|
||||
KeyCtrlShiftLeft: "ctrl+shift+left",
|
||||
KeyCtrlShiftRight: "ctrl+shift+right",
|
||||
KeyF1: "f1",
|
||||
KeyF2: "f2",
|
||||
KeyF3: "f3",
|
||||
KeyF4: "f4",
|
||||
KeyF5: "f5",
|
||||
KeyF6: "f6",
|
||||
KeyF7: "f7",
|
||||
KeyF8: "f8",
|
||||
KeyF9: "f9",
|
||||
KeyF10: "f10",
|
||||
KeyF11: "f11",
|
||||
KeyF12: "f12",
|
||||
KeyF13: "f13",
|
||||
KeyF14: "f14",
|
||||
KeyF15: "f15",
|
||||
KeyF16: "f16",
|
||||
KeyF17: "f17",
|
||||
KeyF18: "f18",
|
||||
KeyF19: "f19",
|
||||
KeyF20: "f20",
|
||||
}
|
||||
|
||||
// Sequence mappings.
|
||||
var sequences = map[string]Key{
|
||||
"\x1b[A": {Type: KeyUp},
|
||||
"\x1b[B": {Type: KeyDown},
|
||||
"\x1b[C": {Type: KeyRight},
|
||||
"\x1b[D": {Type: KeyLeft},
|
||||
// Arrow keys
|
||||
"\x1b[A": {Type: KeyUp},
|
||||
"\x1b[B": {Type: KeyDown},
|
||||
"\x1b[C": {Type: KeyRight},
|
||||
"\x1b[D": {Type: KeyLeft},
|
||||
"\x1b[1;2A": {Type: KeyShiftUp},
|
||||
"\x1b[1;2B": {Type: KeyShiftDown},
|
||||
"\x1b[1;2C": {Type: KeyShiftRight},
|
||||
"\x1b[1;2D": {Type: KeyShiftLeft},
|
||||
"\x1b[OA": {Type: KeyShiftUp}, // DECCKM
|
||||
"\x1b[OB": {Type: KeyShiftDown}, // DECCKM
|
||||
"\x1b[OC": {Type: KeyShiftRight}, // DECCKM
|
||||
"\x1b[OD": {Type: KeyShiftLeft}, // DECCKM
|
||||
"\x1b[a": {Type: KeyShiftUp}, // urxvt
|
||||
"\x1b[b": {Type: KeyShiftDown}, // urxvt
|
||||
"\x1b[c": {Type: KeyShiftRight}, // urxvt
|
||||
"\x1b[d": {Type: KeyShiftLeft}, // urxvt
|
||||
"\x1b[1;3A": {Type: KeyUp, Alt: true},
|
||||
"\x1b[1;3B": {Type: KeyDown, Alt: true},
|
||||
"\x1b[1;3C": {Type: KeyRight, Alt: true},
|
||||
"\x1b[1;3D": {Type: KeyLeft, Alt: true},
|
||||
"\x1b\x1b[A": {Type: KeyUp, Alt: true}, // urxvt
|
||||
"\x1b\x1b[B": {Type: KeyDown, Alt: true}, // urxvt
|
||||
"\x1b\x1b[C": {Type: KeyRight, Alt: true}, // urxvt
|
||||
"\x1b\x1b[D": {Type: KeyLeft, Alt: true}, // urxvt
|
||||
"\x1b[1;4A": {Type: KeyShiftUp, Alt: true},
|
||||
"\x1b[1;4B": {Type: KeyShiftDown, Alt: true},
|
||||
"\x1b[1;4C": {Type: KeyShiftRight, Alt: true},
|
||||
"\x1b[1;4D": {Type: KeyShiftLeft, Alt: true},
|
||||
"\x1b\x1b[a": {Type: KeyShiftUp, Alt: true}, // urxvt
|
||||
"\x1b\x1b[b": {Type: KeyShiftDown, Alt: true}, // urxvt
|
||||
"\x1b\x1b[c": {Type: KeyShiftRight, Alt: true}, // urxvt
|
||||
"\x1b\x1b[d": {Type: KeyShiftLeft, Alt: true}, // urxvt
|
||||
"\x1b[1;5A": {Type: KeyCtrlUp},
|
||||
"\x1b[1;5B": {Type: KeyCtrlDown},
|
||||
"\x1b[1;5C": {Type: KeyCtrlRight},
|
||||
"\x1b[1;5D": {Type: KeyCtrlLeft},
|
||||
"\x1b[Oa": {Type: KeyCtrlUp, Alt: true}, // urxvt
|
||||
"\x1b[Ob": {Type: KeyCtrlDown, Alt: true}, // urxvt
|
||||
"\x1b[Oc": {Type: KeyCtrlRight, Alt: true}, // urxvt
|
||||
"\x1b[Od": {Type: KeyCtrlLeft, Alt: true}, // urxvt
|
||||
"\x1b[1;6A": {Type: KeyCtrlShiftUp},
|
||||
"\x1b[1;6B": {Type: KeyCtrlShiftDown},
|
||||
"\x1b[1;6C": {Type: KeyCtrlShiftRight},
|
||||
"\x1b[1;6D": {Type: KeyCtrlShiftLeft},
|
||||
"\x1b[1;7A": {Type: KeyCtrlUp, Alt: true},
|
||||
"\x1b[1;7B": {Type: KeyCtrlDown, Alt: true},
|
||||
"\x1b[1;7C": {Type: KeyCtrlRight, Alt: true},
|
||||
"\x1b[1;7D": {Type: KeyCtrlLeft, Alt: true},
|
||||
"\x1b[1;8A": {Type: KeyCtrlShiftUp, Alt: true},
|
||||
"\x1b[1;8B": {Type: KeyCtrlShiftDown, Alt: true},
|
||||
"\x1b[1;8C": {Type: KeyCtrlShiftRight, Alt: true},
|
||||
"\x1b[1;8D": {Type: KeyCtrlShiftLeft, Alt: true},
|
||||
|
||||
// Miscellaneous keys
|
||||
"\x1b[Z": {Type: KeyShiftTab},
|
||||
"\x1b[3~": {Type: KeyDelete},
|
||||
"\x1b[3;3~": {Type: KeyDelete, Alt: true},
|
||||
"\x1b[1~": {Type: KeyHome},
|
||||
"\x1b[1;3H~": {Type: KeyHome, Alt: true},
|
||||
"\x1b[4~": {Type: KeyEnd},
|
||||
"\x1b[1;3F~": {Type: KeyEnd, Alt: true},
|
||||
"\x1b[5~": {Type: KeyPgUp},
|
||||
"\x1b[5;3~": {Type: KeyPgUp, Alt: true},
|
||||
"\x1b[6~": {Type: KeyPgDown},
|
||||
"\x1b[6;3~": {Type: KeyPgDown, Alt: true},
|
||||
"\x1b[7~": {Type: KeyHome}, // urxvt
|
||||
"\x1b[8~": {Type: KeyEnd}, // urxvt
|
||||
"\x1b\x1b[3~": {Type: KeyDelete, Alt: true}, // urxvt
|
||||
"\x1b\x1b[5~": {Type: KeyPgUp, Alt: true}, // urxvt
|
||||
"\x1b\x1b[6~": {Type: KeyPgDown, Alt: true}, // urxvt
|
||||
"\x1b\x1b[7~": {Type: KeyHome, Alt: true}, // urxvt
|
||||
"\x1b\x1b[8~": {Type: KeyEnd, Alt: true}, // urxvt
|
||||
|
||||
// Function keys, X11
|
||||
"\x1bOP": {Type: KeyF1}, // vt100
|
||||
@@ -367,28 +460,8 @@ var sequences = map[string]Key{
|
||||
|
||||
// Hex code mappings.
|
||||
var hexes = map[string]Key{
|
||||
"1b5b5a": {Type: KeyShiftTab},
|
||||
"1b5b337e": {Type: KeyDelete},
|
||||
"1b0d": {Type: KeyEnter, Alt: true},
|
||||
"1b7f": {Type: KeyBackspace, Alt: true},
|
||||
"1b5b48": {Type: KeyHome},
|
||||
"1b5b377e": {Type: KeyHome}, // urxvt
|
||||
"1b5b313b3348": {Type: KeyHome, Alt: true},
|
||||
"1b1b5b377e": {Type: KeyHome, Alt: true}, // urxvt
|
||||
"1b5b46": {Type: KeyEnd},
|
||||
"1b5b387e": {Type: KeyEnd}, // urxvt
|
||||
"1b5b313b3346": {Type: KeyEnd, Alt: true},
|
||||
"1b1b5b387e": {Type: KeyEnd, Alt: true}, // urxvt
|
||||
"1b5b357e": {Type: KeyPgUp},
|
||||
"1b5b353b337e": {Type: KeyPgUp, Alt: true},
|
||||
"1b1b5b357e": {Type: KeyPgUp, Alt: true}, // urxvt
|
||||
"1b5b367e": {Type: KeyPgDown},
|
||||
"1b5b363b337e": {Type: KeyPgDown, Alt: true},
|
||||
"1b1b5b367e": {Type: KeyPgDown, Alt: true}, // urxvt
|
||||
"1b5b313b3341": {Type: KeyUp, Alt: true},
|
||||
"1b5b313b3342": {Type: KeyDown, Alt: true},
|
||||
"1b5b313b3343": {Type: KeyRight, Alt: true},
|
||||
"1b5b313b3344": {Type: KeyLeft, Alt: true},
|
||||
"1b0d": {Type: KeyEnter, Alt: true},
|
||||
"1b7f": {Type: KeyBackspace, Alt: true},
|
||||
|
||||
// Powershell
|
||||
"1b4f41": {Type: KeyUp, Alt: false},
|
||||
@@ -408,7 +481,7 @@ func readInputs(input io.Reader) ([]Msg, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// See if it's a mouse event. For now we're parsing X10-type mouse events
|
||||
// Check if it's a mouse event. For now we're parsing X10-type mouse events
|
||||
// only.
|
||||
mouseEvent, err := parseX10MouseEvents(buf[:numBytes])
|
||||
if err == nil {
|
||||
@@ -419,14 +492,14 @@ func readInputs(input io.Reader) ([]Msg, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Is it a special sequence, like an arrow key?
|
||||
// Is it a sequence, like an arrow key?
|
||||
if k, ok := sequences[string(buf[:numBytes])]; ok {
|
||||
return []Msg{
|
||||
KeyMsg(k),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Some of these need special handling
|
||||
// Some of these need special handling.
|
||||
hex := fmt.Sprintf("%x", buf[:numBytes])
|
||||
if k, ok := hexes[hex]; ok {
|
||||
return []Msg{
|
||||
@@ -434,8 +507,8 @@ func readInputs(input io.Reader) ([]Msg, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Is the alt key pressed? The buffer will be prefixed with an escape
|
||||
// sequence if so.
|
||||
// Is the alt key pressed? If so, the buffer will be prefixed with an
|
||||
// escape.
|
||||
if numBytes > 1 && buf[0] == 0x1b {
|
||||
// Now remove the initial escape sequence and re-process to get the
|
||||
// character being pressed in combination with alt.
|
||||
@@ -483,7 +556,7 @@ func readInputs(input io.Reader) ([]Msg, error) {
|
||||
|
||||
// If it's a space, override the type with KeySpace (but still include the
|
||||
// rune).
|
||||
if len(runes) == 1 && runes[0] == ' ' {
|
||||
if runes[0] == ' ' {
|
||||
return []Msg{
|
||||
KeyMsg(Key{Type: KeySpace, Runes: runes}),
|
||||
}, nil
|
||||
|
||||
Reference in New Issue
Block a user