Module: Clack::Core::KeyReader

Defined in:
lib/clack/core/key_reader.rb

Overview

Reads single keystrokes from the terminal in raw mode. Handles escape sequences for arrow keys and other special keys.

Escape sequences come in two shapes: CSI (+ESC [+ ... final byte) and SS3 (+ESC O+ + one byte), the latter used by terminals in application cursor mode (tmux, and after vim or less leave keypad-transmit mode on). Both are assembled here and folded into one canonical code per key via Settings.normalize_key, so prompts only ever see "\e[A" for Up.

The Escape detection window is tunable via the CLACK_ESCAPE_TIMEOUT env var (see Environment.escape_timeout) for high-latency links.

Constant Summary collapse

SEQUENCE_INTRODUCERS =

Escape-sequence introducers: CSI ("[") and SS3 ("O").

["[", "O"].freeze
MAX_SEQUENCE_LENGTH =

Upper bound on bytes assembled after the introducer, so a stream of garbage can never keep the reader inside one sequence forever.

32
CSI_FINAL_BYTES =

Range of CSI final bytes (+@+ through ~), per ECMA-48.

(0x40..0x7E)

Class Method Summary collapse

Class Method Details

.read(input = nil) ⇒ String

Read a single keystroke in raw mode. When input is an IO backed by a console, uses raw mode. When input is a StringIO or test double, reads directly.

End of input (EOF) and a disconnected terminal both return Ctrl+C so the prompt cancels instead of spinning on an exhausted stream.

Parameters:

  • input (IO, nil) (defaults to: nil)

    input stream (defaults to IO.console)

Returns:

  • (String)

    the (normalized) key code

Raises:



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/clack/core/key_reader.rb', line 40

def read(input = nil)
  io = input || IO.console
  raise NotATerminalError, not_a_terminal_message(io) unless io

  key = io.respond_to?(:raw) ? read_raw(io) : read_from(io)
  Settings.normalize_key(key || Settings::KEY_CTRL_C)
rescue NotATerminalError
  raise
rescue Errno::EIO, Errno::EBADF, IOError
  # Terminal disconnected or closed - treat as cancel
  Settings::KEY_CTRL_C
end