package keymap import ( "testing" uv "github.com/charmbracelet/ultraviolet" ) func TestParseEvents(t *testing.T) { cases := []struct { name string want uv.KeyPressEvent }{ {"Enter", uv.KeyPressEvent{Code: uv.KeyEnter}}, {"enter", uv.KeyPressEvent{Code: uv.KeyEnter}}, {"Escape", uv.KeyPressEvent{Code: uv.KeyEscape}}, {"Esc", uv.KeyPressEvent{Code: uv.KeyEscape}}, {"Tab", uv.KeyPressEvent{Code: uv.KeyTab}}, {"BTab", uv.KeyPressEvent{Code: uv.KeyTab, Mod: uv.ModShift}}, {"S-Tab", uv.KeyPressEvent{Code: uv.KeyTab, Mod: uv.ModShift}}, {"Backspace", uv.KeyPressEvent{Code: uv.KeyBackspace}}, {"Space", uv.KeyPressEvent{Code: ' '}}, {"Up", uv.KeyPressEvent{Code: uv.KeyUp}}, {"Down", uv.KeyPressEvent{Code: uv.KeyDown}}, {"Left", uv.KeyPressEvent{Code: uv.KeyLeft}}, {"Right", uv.KeyPressEvent{Code: uv.KeyRight}}, {"Home", uv.KeyPressEvent{Code: uv.KeyHome}}, {"End", uv.KeyPressEvent{Code: uv.KeyEnd}}, {"PgUp", uv.KeyPressEvent{Code: uv.KeyPgUp}}, {"PageDown", uv.KeyPressEvent{Code: uv.KeyPgDown}}, {"Insert", uv.KeyPressEvent{Code: uv.KeyInsert}}, {"Delete", uv.KeyPressEvent{Code: uv.KeyDelete}}, {"F1", uv.KeyPressEvent{Code: uv.KeyF1}}, {"F12", uv.KeyPressEvent{Code: uv.KeyF12}}, {"a", uv.KeyPressEvent{Code: 'a'}}, {"Z", uv.KeyPressEvent{Code: 'Z'}}, {"%", uv.KeyPressEvent{Code: '%'}}, {"C-c", uv.KeyPressEvent{Code: 'c', Mod: uv.ModCtrl}}, {"C-C", uv.KeyPressEvent{Code: 'c', Mod: uv.ModCtrl}}, // normalized to lowercase {"C-Space", uv.KeyPressEvent{Code: ' ', Mod: uv.ModCtrl}}, {"C-[", uv.KeyPressEvent{Code: '[', Mod: uv.ModCtrl}}, {"C-]", uv.KeyPressEvent{Code: ']', Mod: uv.ModCtrl}}, {"C-_", uv.KeyPressEvent{Code: '_', Mod: uv.ModCtrl}}, {"M-x", uv.KeyPressEvent{Code: 'x', Mod: uv.ModAlt}}, {"M-Enter", uv.KeyPressEvent{Code: uv.KeyEnter, Mod: uv.ModAlt}}, {"M-Escape", uv.KeyPressEvent{Code: uv.KeyEscape, Mod: uv.ModAlt}}, {"C-M-x", uv.KeyPressEvent{Code: 'x', Mod: uv.ModCtrl | uv.ModAlt}}, {"M-C-x", uv.KeyPressEvent{Code: 'x', Mod: uv.ModCtrl | uv.ModAlt}}, {"S-a", uv.KeyPressEvent{Code: 'A'}}, {"C-S-a", uv.KeyPressEvent{Code: 'a', Mod: uv.ModCtrl}}, // shift folds into the letter, ctrl re-lowers {"-", uv.KeyPressEvent{Code: '-'}}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { k, err := Parse(tc.name) if err != nil { t.Fatalf("Parse(%q): %v", tc.name, err) } ev, ok := k.Event() if !ok { raw, _ := k.Raw() t.Fatalf("Parse(%q) produced raw bytes %q, want event", tc.name, raw) } if ev != tc.want { t.Errorf("Parse(%q) = %+v, want %+v", tc.name, ev, tc.want) } }) } } // Modified special keys have no vt-encoder support and must come out as // pre-encoded xterm sequences: CSI 1;m and CSI n;m~ with // m = 1 + Shift(1) + Alt(2) + Ctrl(4). func TestParseModifiedSpecials(t *testing.T) { cases := []struct { name string want string }{ {"C-Up", "\x1b[1;5A"}, {"S-Up", "\x1b[1;2A"}, {"M-Up", "\x1b[1;3A"}, {"C-S-Up", "\x1b[1;6A"}, {"C-M-Up", "\x1b[1;7A"}, {"C-M-S-Up", "\x1b[1;8A"}, {"C-Down", "\x1b[1;5B"}, {"C-Right", "\x1b[1;5C"}, {"C-Left", "\x1b[1;5D"}, {"C-Home", "\x1b[1;5H"}, {"S-End", "\x1b[1;2F"}, {"C-F1", "\x1b[1;5P"}, {"S-F4", "\x1b[1;2S"}, {"C-Delete", "\x1b[3;5~"}, {"S-Insert", "\x1b[2;2~"}, {"M-PgUp", "\x1b[5;3~"}, {"C-PgDn", "\x1b[6;5~"}, {"C-F5", "\x1b[15;5~"}, {"S-F12", "\x1b[24;2~"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { k, err := Parse(tc.name) if err != nil { t.Fatalf("Parse(%q): %v", tc.name, err) } raw, ok := k.Raw() if !ok { ev, _ := k.Event() t.Fatalf("Parse(%q) produced event %+v, want raw bytes", tc.name, ev) } if string(raw) != tc.want { t.Errorf("Parse(%q) = %q, want %q", tc.name, raw, tc.want) } }) } } func TestParseErrors(t *testing.T) { for _, name := range []string{ "", // empty "NoSuch", // unknown named key "C-", // dangling modifier "X-a", // bad modifier letter "C-1", // no control character for digits "C--", // no control character for dash "S-5", // shift is only meaningful on letters "C-Enter", // no escape sequence exists "ab", // multi-rune non-name } { t.Run("bad:"+name, func(t *testing.T) { if k, err := Parse(name); err == nil { t.Errorf("Parse(%q) succeeded (%+v), want error", name, k) } }) } }