From 01a81100bbd30782c6fcd262fb80b4ccc7db3134 Mon Sep 17 00:00:00 2001 From: Lixen Wraith Date: Fri, 16 Oct 2026 23:48:40 -0400 Subject: [PATCH] v0.4.1 export control key --- controlkey_test.go | 26 ++++++++++++++++++++++++++ input.go | 27 +++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 controlkey_test.go diff --git a/controlkey_test.go b/controlkey_test.go new file mode 100644 index 0000000..8d295a2 --- /dev/null +++ b/controlkey_test.go @@ -0,0 +1,26 @@ +package terminal + +import "testing" + +func TestControlKey(t *testing.T) { + cases := map[rune]Key{ + 'a': KeyCtrlA, 'G': KeyCtrlG, 'z': KeyCtrlZ, + 'h': KeyBackspace, 'i': KeyTab, 'j': KeyEnter, 'm': KeyEnter, + ' ': KeyCtrlSpace, '@': KeyCtrlSpace, '2': KeyCtrlSpace, + '[': KeyEscape, '3': KeyEscape, + '\\': KeyCtrlBackslash, '4': KeyCtrlBackslash, + ']': KeyCtrlBracketRight, '5': KeyCtrlBracketRight, + '^': KeyCtrlCaret, '6': KeyCtrlCaret, + '_': KeyCtrlUnderscore, '-': KeyCtrlUnderscore, '/': KeyCtrlUnderscore, '7': KeyCtrlUnderscore, + } + for r, want := range cases { + if got, ok := ControlKey(r); !ok || got != want { + t.Errorf("ControlKey(%q) = %v, %v; want %v, true", r, got, ok, want) + } + } + for _, r := range []rune{'1', '8', '.', '~', 'é', 0} { + if _, ok := ControlKey(r); ok { + t.Errorf("ControlKey(%q) ok, want none", r) + } + } +} diff --git a/input.go b/input.go index f0df248..caa87e9 100644 --- a/input.go +++ b/input.go @@ -663,3 +663,30 @@ func decodeRune(data []byte) (rune, int) { return r, size } + +// ControlKey is the key a terminal reads for Ctrl with a printable character: the +// control byte that pair sends, parsed as input parsing reads it. Letters of +// either case, space and '@', '[' '\' ']' '^' '_', '-' and '/' as '_', and the +// digits 2 to 7 have one; ok is false for any other character. +func ControlKey(r rune) (Key, bool) { + var b byte + switch { + case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z': + b = byte(r) & 0x1f + case r == ' ', r == '@', r == '2': + b = 0x00 + case r == '[', r == '3': + b = 0x1b + case r == '\\', r == '4': + b = 0x1c + case r == ']', r == '5': + b = 0x1d + case r == '^', r == '6': + b = 0x1e + case r == '_', r == '-', r == '/', r == '7': + b = 0x1f + default: + return KeyNone, false + } + return (*inputReader)(nil).parseControl(b).Key, true +}