From 5c14a829aa4c0f25181b5ce0de8cfcaa26957fdd Mon Sep 17 00:00:00 2001 From: sneak Date: Wed, 22 Jul 2026 22:08:59 +0700 Subject: [PATCH] Decompose tcell ReadChar key translation (refactor step 7) ReadChar's key switch moves into translateKey with namedKey split into motionKey/editingKey halves. term is complexity-clean. Behavior unchanged. --- term/tcell.go | 114 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 37 deletions(-) diff --git a/term/tcell.go b/term/tcell.go index 8d68857..d87a705 100644 --- a/term/tcell.go +++ b/term/tcell.go @@ -89,48 +89,88 @@ func (t *Tcell) ReadChar() byte { t.Render(t.last) } case *tcell.EventKey: - switch ev.Key() { - case tcell.KeyUp: - return 'k' - case tcell.KeyDown: - return 'j' - case tcell.KeyLeft: - return 'h' - case tcell.KeyRight: - return 'l' - case tcell.KeyHome: - return 'y' - case tcell.KeyPgUp: - return 'u' - case tcell.KeyEnd: - return 'b' - case tcell.KeyPgDn: - return 'n' - case tcell.KeyEnter: - return '\n' - case tcell.KeyEscape: - return game.Escape - case tcell.KeyBackspace, tcell.KeyBackspace2: - return 8 - case tcell.KeyDelete: - return 0x7f - case tcell.KeyTab: - return '\t' - case tcell.KeyCtrlC: - return 3 - default: - if ev.Key() >= tcell.KeyCtrlA && ev.Key() <= tcell.KeyCtrlZ { - return byte(ev.Key()) //nolint:gosec // G115: 1..26 fits - } - - if r := ev.Rune(); r > 0 && r < 0x80 { - return byte(r) - } + if b, ok := translateKey(ev); ok { + return b } } } } +// translateKey converts a key event to a game input byte; ok is false +// for keys the C game does not understand. +func translateKey(ev *tcell.EventKey) (byte, bool) { + if b, ok := namedKey(ev.Key()); ok { + return b, true + } + + if ev.Key() >= tcell.KeyCtrlA && ev.Key() <= tcell.KeyCtrlZ { + return byte(ev.Key()), true //nolint:gosec // G115: 1..26 fits + } + + if r := ev.Rune(); r > 0 && r < 0x80 { + return byte(r), true + } + + return 0, false +} + +// namedKey translates tcell's navigation and editing keys to the single +// bytes the C game reads (arrows become hjkl, etc.); ok is false for +// keys handled elsewhere. +func namedKey(k tcell.Key) (byte, bool) { + if b, ok := motionKey(k); ok { + return b, true + } + + return editingKey(k) +} + +// motionKey translates the arrow and paging keys to Rogue's movement +// letters (tcell.go ReadChar). +func motionKey(k tcell.Key) (byte, bool) { + switch k { + case tcell.KeyUp: + return 'k', true + case tcell.KeyDown: + return 'j', true + case tcell.KeyLeft: + return 'h', true + case tcell.KeyRight: + return 'l', true + case tcell.KeyHome: + return 'y', true + case tcell.KeyPgUp: + return 'u', true + case tcell.KeyEnd: + return 'b', true + case tcell.KeyPgDn: + return 'n', true + } + + return 0, false +} + +// editingKey translates the editing and control keys to their C0 codes +// (tcell.go ReadChar). +func editingKey(k tcell.Key) (byte, bool) { + switch k { + case tcell.KeyEnter: + return '\n', true + case tcell.KeyEscape: + return game.Escape, true + case tcell.KeyBackspace, tcell.KeyBackspace2: + return 8, true + case tcell.KeyDelete: + return 0x7f, true + case tcell.KeyTab: + return '\t', true + case tcell.KeyCtrlC: + return 3, true + } + + return 0, false +} + // ShellEscape suspends the screen and runs the user's shell (main.c // shell + md_shellescape). func (t *Tcell) ShellEscape() {