celina/core/key_logic

Key Event Processing Logic

This module contains pure business logic for key event processing, shared between synchronous and asynchronous implementations.

No I/O operations are performed here - only character-to-key mappings.

Types

CtrlKeyResult = object
  isCtrlKey*: bool
  keyEvent*: KeyEvent
Result of Ctrl key mapping
KeyCode = enum
  Char, Enter, Escape, Backspace, Tab, BackTab, Space, ArrowUp, ArrowDown,
  ArrowLeft, ArrowRight, Home, End, PageUp, PageDown, Insert, Delete, F1, F2,
  F3, F4, F5, F6, F7, F8, F9, F10, F11, F12
KeyEvent = object
  code*: KeyCode
  char*: string
  modifiers*: set[KeyModifier]

Procs

proc applyModifiers(keyEvent: KeyEvent; modifiers: set[KeyModifier]): KeyEvent {.
    ...raises: [], tags: [], forbids: [].}

Apply modifiers to a key event

Example:

let key = KeyEvent(code: ArrowUp, char: "")
let modified = applyModifiers(key, {Ctrl, Shift})
assert modified.modifiers == {Ctrl, Shift}

proc mapArrowKey(ch: char): KeyEvent {....raises: [], tags: [], forbids: [].}

Map escape sequence final character to arrow key

ESC [ A/B/C/D -> ArrowUp/Down/Right/Left

Example:

assert mapArrowKey('A').code == ArrowUp
assert mapArrowKey('B').code == ArrowDown

proc mapBasicKey(ch: char): KeyEvent {....raises: [], tags: [], forbids: [].}

Map basic characters to key events

Handles: Enter, Tab, Space, Backspace, and regular characters

Example:

assert mapBasicKey('\r').code == Enter
assert mapBasicKey('\t').code == Tab
assert mapBasicKey('a').code == Char

proc mapCtrlLetterKey(ch: char): CtrlKeyResult {....raises: [], tags: [],
    forbids: [].}

Map Ctrl+letter combinations (x01-x1a) to key events

x01-x1a = Ctrl-A to Ctrl-Z, but exclude already-handled keys: x03 = Ctrl-C (Quit), x08 = Ctrl-H (Backspace), x09 = Ctrl-I (Tab) x0a = Ctrl-J (Line Feed), x0d = Ctrl-M (Enter), x1b = Ctrl-[ (Escape)

Example:

let result = mapCtrlLetterKey('\x01')  # Ctrl-A
assert result.isCtrlKey
assert result.keyEvent.char == "a"
assert result.keyEvent.modifiers == {Ctrl}

proc mapCtrlNumberKey(ch: char): CtrlKeyResult {....raises: [], tags: [],
    forbids: [].}

Map Ctrl+number and special control characters

Following crossterm's mapping: x00 = Ctrl-Space, x1c = Ctrl-4, x1d = Ctrl-5, x1e = Ctrl-6, x1f = Ctrl-7

Example:

let result = mapCtrlNumberKey('\x00')  # Ctrl-Space
assert result.isCtrlKey
assert result.keyEvent.code == Space

proc mapFunctionKey(sequence: string): KeyEvent {....raises: [], tags: [],
    forbids: [].}

Map multi-digit escape sequences for function keys

Function keys use ESC [ nn ~ format: F1-F4: ESC [ 11~, 12~, 13~, 14~ (alternative to ESC O P/Q/R/S) F5-F12: ESC [ 15~, 17~, 18~, 19~, 20~, 21~, 23~, 24~

Example:

assert mapFunctionKey("11").code == F1
assert mapFunctionKey("15").code == F5

proc mapNavigationKey(ch: char): KeyEvent {....raises: [], tags: [], forbids: [].}

Map escape sequence final character to navigation key

ESC [ H/F/Z -> Home/End/BackTab

proc mapNumericKeyCode(numChar: char): KeyEvent {....raises: [], tags: [],
    forbids: [].}

Map numeric escape sequences ESC [ n ~

1/4 = Home/End, 2 = Insert, 3 = Delete, 5/6 = PageUp/PageDown

Example:

assert mapNumericKeyCode('1').code == Home
assert mapNumericKeyCode('2').code == Insert

proc mapVT100FunctionKey(ch: char): KeyEvent {....raises: [], tags: [], forbids: [].}

Map VT100-style function keys ESC O P/Q/R/S

This is the older format for F1-F4: P = F1, Q = F2, R = F3, S = F4

Example:

assert mapVT100FunctionKey('P').code == F1
assert mapVT100FunctionKey('Q').code == F2

proc parseModifierCode(modChar: char): set[KeyModifier] {....raises: [], tags: [],
    forbids: [].}

Parse modifier code from escape sequence

Modifier encoding (1-based, subtract 1 for bit flags): 1=None, 2=Shift, 3=Alt, 4=Shift+Alt, 5=Ctrl, 6=Ctrl+Shift, 7=Ctrl+Alt, 8=Ctrl+Shift+Alt

Example:

assert parseModifierCode('2') == {Shift}
assert parseModifierCode('5') == {Ctrl}
assert parseModifierCode('8') == {Ctrl, Shift, Alt}