Class: Charming::Internal::Terminal::MouseParser
- Inherits:
-
Object
- Object
- Charming::Internal::Terminal::MouseParser
- Defined in:
- lib/charming/internal/terminal/mouse_parser.rb
Overview
MouseParser parses raw terminal escape sequences into MouseEvent objects. Supports both modern SGR sequences (the most common, used by current terminals) and the older 3-byte legacy sequences. The public API is class methods; no instance state is required.
Constant Summary collapse
- SGR_PATTERN =
Matches an SGR-encoded mouse sequence: “e[<button;col;rowM”
/\e\[<(\d+);(\d+);(\d+)([HmMhCc]?)(M|m)/- LEGACY_PATTERN =
Matches the legacy 3-byte mouse sequence: “e[M” followed by 3 bytes.
/\e\[M(.{3})/- BUTTON_MAP =
Maps raw button codes to semantic symbols used by MouseEvent#button_name.
{ 0 => :left, 1 => :middle, 2 => :right, 3 => :release, 64 => :scroll_up, 65 => :scroll_down, 66 => :scroll_up, 67 => :scroll_down }.freeze
Class Method Summary collapse
-
.parse(raw) ⇒ Object
Parses raw into a MouseEvent, or returns nil when the string is not a mouse sequence or cannot be decoded.
-
.parse_legacy(raw) ⇒ Object
Parses a legacy 3-byte mouse sequence.
-
.parse_sgr(raw) ⇒ Object
Parses an SGR-format mouse sequence.
-
.sequence?(raw) ⇒ Boolean
Returns true when raw looks like a recognizable mouse sequence (SGR or legacy).
Class Method Details
.parse(raw) ⇒ Object
Parses raw into a MouseEvent, or returns nil when the string is not a mouse sequence or cannot be decoded.
36 37 38 39 40 41 42 |
# File 'lib/charming/internal/terminal/mouse_parser.rb', line 36 def self.parse(raw) return nil unless raw.is_a?(String) return parse_sgr(raw) if raw.match?(SGR_PATTERN) return parse_legacy(raw) if raw.start_with?("\e[M") nil end |
.parse_legacy(raw) ⇒ Object
Parses a legacy 3-byte mouse sequence. Each of the 3 bytes has 32 subtracted to recover the (button, col, row) values.
65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/charming/internal/terminal/mouse_parser.rb', line 65 def self.parse_legacy(raw) match = raw.match(LEGACY_PATTERN) return nil unless match bytes = match[1].bytes return nil unless bytes.length == 3 = bytes[0] - 32 col = bytes[1] - 32 row = bytes[2] - 32 Events::MouseEvent.new(button: , x: col, y: row) end |
.parse_sgr(raw) ⇒ Object
Parses an SGR-format mouse sequence. Decodes button code, 1-based (col, row), the modifier “C” (ctrl) and “M” (shift) suffix, and the highlight alt (256-color) sequence as a heuristic for the alt modifier.
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/charming/internal/terminal/mouse_parser.rb', line 47 def self.parse_sgr(raw) match = raw.match(SGR_PATTERN) return nil unless match = match[1].to_i col = match[2].to_i - 1 row = match[3].to_i - 1 mode = match[4] ctrl = mode == "C" alt = raw.include?("\e[38;5;") shift = mode == "M" Events::MouseEvent.new(button: , x: col, y: row, ctrl: ctrl, alt: alt, shift: shift) end |
.sequence?(raw) ⇒ Boolean
Returns true when raw looks like a recognizable mouse sequence (SGR or legacy). Lets the TTYBackend short-circuit and dispatch to MouseParser without allocation.
26 27 28 29 30 31 32 |
# File 'lib/charming/internal/terminal/mouse_parser.rb', line 26 def self.sequence?(raw) return false unless raw.is_a?(String) return true if raw.match?(SGR_PATTERN) return true if raw.start_with?("\e[M") false end |