Python · 5742 bytes Raw Blame History
1 """
2 Key sequence definitions for terminal input simulation.
3
4 These escape sequences follow standard xterm/VT100 conventions.
5 """
6
7 # Arrow keys (CSI sequences)
8 ARROW_UP = "\x1b[A"
9 ARROW_DOWN = "\x1b[B"
10 ARROW_RIGHT = "\x1b[C"
11 ARROW_LEFT = "\x1b[D"
12
13 # Shift-modified arrow / nav keys (xterm modifier 2 = Shift)
14 # Used for native text selection (shift phase, Sprint 1+).
15 SHIFT_ARROW_UP = "\x1b[1;2A"
16 SHIFT_ARROW_DOWN = "\x1b[1;2B"
17 SHIFT_ARROW_RIGHT = "\x1b[1;2C"
18 SHIFT_ARROW_LEFT = "\x1b[1;2D"
19 SHIFT_HOME = "\x1b[1;2H"
20 SHIFT_END = "\x1b[1;2F"
21
22 # Ctrl+Shift arrow (xterm modifier 6) — word-wise selection extension
23 CTRL_SHIFT_ARROW_RIGHT = "\x1b[1;6C"
24 CTRL_SHIFT_ARROW_LEFT = "\x1b[1;6D"
25
26 # Alt+Shift letter (ESC + uppercase) — emacs-native word-wise selection
27 ALT_SHIFT_B = "\x1bB"
28 ALT_SHIFT_F = "\x1bF"
29
30 # Alt+Shift arrow (xterm modifier 4) — fortsh binds these to dir history
31 ALT_SHIFT_UP = "\x1b[1;4A"
32 ALT_SHIFT_DOWN = "\x1b[1;4B"
33 ALT_SHIFT_LEFT = "\x1b[1;4D"
34 ALT_SHIFT_RIGHT = "\x1b[1;4C"
35
36 # Control keys (ASCII control characters)
37 CTRL_A = "\x01" # Beginning of line
38 CTRL_B = "\x02" # Back one character
39 CTRL_C = "\x03" # Interrupt (SIGINT)
40 CTRL_D = "\x04" # Delete char / EOF
41 CTRL_E = "\x05" # End of line
42 CTRL_F = "\x06" # Forward one character
43 CTRL_G = "\x07" # Cancel
44 CTRL_H = "\x08" # Backspace (alternative)
45 CTRL_I = "\x09" # Tab
46 CTRL_J = "\x0a" # Newline
47 CTRL_K = "\x0b" # Kill to end of line
48 CTRL_L = "\x0c" # Clear screen
49 CTRL_M = "\x0d" # Carriage return (Enter)
50 CTRL_N = "\x0e" # Next history
51 CTRL_O = "\x0f" # Operate and get next
52 CTRL_P = "\x10" # Previous history
53 CTRL_Q = "\x11" # Resume output
54 CTRL_R = "\x12" # Reverse search
55 CTRL_S = "\x13" # Forward search / Suspend output
56 CTRL_T = "\x14" # Transpose characters
57 CTRL_U = "\x15" # Kill to beginning of line
58 CTRL_V = "\x16" # Paste from clipboard / kill buffer
59 CTRL_W = "\x17" # Kill word backward / cut selection
60 CTRL_X = "\x18" # Cut selection / process kill mode
61 CTRL_Y = "\x19" # Yank
62 CTRL_Z = "\x1a" # Suspend (SIGTSTP)
63
64 # Alt/Meta keys (ESC + key)
65 ALT_B = "\x1bb" # Back one word
66 ALT_C = "\x1bc" # Capitalize word
67 ALT_D = "\x1bd" # Delete word forward
68 ALT_F = "\x1bf" # Forward one word
69 ALT_L = "\x1bl" # Lowercase word
70 ALT_T = "\x1bt" # Transpose words
71 ALT_U = "\x1bu" # Uppercase word
72 ALT_W = "\x1bw" # Copy selection (shift phase) / Accept word from suggestion
73 ALT_Y = "\x1by" # Yank pop
74 ALT_DOT = "\x1b." # Insert last argument
75 ALT_BACKSPACE = "\x1b\x7f" # Delete word backward
76
77 # Special keys
78 TAB = "\t"
79 ENTER = "\r"
80 NEWLINE = "\n"
81 BACKSPACE = "\x7f"
82 ESCAPE = "\x1b"
83
84 # Extended keys (xterm sequences)
85 DELETE = "\x1b[3~"
86 HOME = "\x1b[H"
87 END = "\x1b[F"
88 PAGE_UP = "\x1b[5~"
89 PAGE_DOWN = "\x1b[6~"
90 INSERT = "\x1b[2~"
91
92 # Alternative Home/End sequences (some terminals use these)
93 HOME_ALT = "\x1b[1~"
94 END_ALT = "\x1b[4~"
95
96 # Function keys
97 F1 = "\x1bOP"
98 F2 = "\x1bOQ"
99 F3 = "\x1bOR"
100 F4 = "\x1bOS"
101 F5 = "\x1b[15~"
102 F6 = "\x1b[17~"
103 F7 = "\x1b[18~"
104 F8 = "\x1b[19~"
105 F9 = "\x1b[20~"
106 F10 = "\x1b[21~"
107 F11 = "\x1b[23~"
108 F12 = "\x1b[24~"
109
110 # Key name to sequence mapping (for YAML test specs)
111 KEYS = {
112 # Arrow keys
113 "Up": ARROW_UP,
114 "Down": ARROW_DOWN,
115 "Right": ARROW_RIGHT,
116 "Left": ARROW_LEFT,
117
118 # Shift-modified navigation (for native text selection)
119 "S-Up": SHIFT_ARROW_UP,
120 "S-Down": SHIFT_ARROW_DOWN,
121 "S-Right": SHIFT_ARROW_RIGHT,
122 "S-Left": SHIFT_ARROW_LEFT,
123 "S-Home": SHIFT_HOME,
124 "S-End": SHIFT_END,
125
126 # Ctrl+Shift navigation (word-wise selection)
127 "C-S-Right": CTRL_SHIFT_ARROW_RIGHT,
128 "C-S-Left": CTRL_SHIFT_ARROW_LEFT,
129
130 # Alt+Shift letter (emacs word-wise selection)
131 "M-B": ALT_SHIFT_B,
132 "M-F": ALT_SHIFT_F,
133
134 # Alt+Shift arrows (fortsh directory history, NOT selection)
135 "M-S-Up": ALT_SHIFT_UP,
136 "M-S-Down": ALT_SHIFT_DOWN,
137 "M-S-Left": ALT_SHIFT_LEFT,
138 "M-S-Right": ALT_SHIFT_RIGHT,
139
140 # Control keys
141 "C-a": CTRL_A,
142 "C-b": CTRL_B,
143 "C-c": CTRL_C,
144 "C-d": CTRL_D,
145 "C-e": CTRL_E,
146 "C-f": CTRL_F,
147 "C-g": CTRL_G,
148 "C-h": CTRL_H,
149 "C-k": CTRL_K,
150 "C-l": CTRL_L,
151 "C-n": CTRL_N,
152 "C-p": CTRL_P,
153 "C-r": CTRL_R,
154 "C-s": CTRL_S,
155 "C-t": CTRL_T,
156 "C-u": CTRL_U,
157 "C-v": CTRL_V,
158 "C-w": CTRL_W,
159 "C-x": CTRL_X,
160 "C-y": CTRL_Y,
161 "C-z": CTRL_Z,
162
163 # Alt/Meta keys
164 "M-b": ALT_B,
165 "M-c": ALT_C,
166 "M-d": ALT_D,
167 "M-f": ALT_F,
168 "M-l": ALT_L,
169 "M-t": ALT_T,
170 "M-u": ALT_U,
171 "M-w": ALT_W,
172 "M-y": ALT_Y,
173 "M-.": ALT_DOT,
174 "M-Backspace": ALT_BACKSPACE,
175
176 # Special keys
177 "Tab": TAB,
178 "Enter": ENTER,
179 "Return": ENTER,
180 "Backspace": BACKSPACE,
181 "Delete": DELETE,
182 "Home": HOME,
183 "End": END,
184 "PageUp": PAGE_UP,
185 "PageDown": PAGE_DOWN,
186 "Insert": INSERT,
187 "Escape": ESCAPE,
188 "Esc": ESCAPE,
189
190 # Function keys
191 "F1": F1,
192 "F2": F2,
193 "F3": F3,
194 "F4": F4,
195 "F5": F5,
196 "F6": F6,
197 "F7": F7,
198 "F8": F8,
199 "F9": F9,
200 "F10": F10,
201 "F11": F11,
202 "F12": F12,
203 }
204
205
206 def get_key(name: str) -> str:
207 """
208 Get the escape sequence for a key name.
209
210 Args:
211 name: Key name (e.g., "Up", "C-a", "M-f", "Enter")
212
213 Returns:
214 The escape sequence for that key
215
216 Raises:
217 KeyError: If the key name is not recognized
218 """
219 if name not in KEYS:
220 raise KeyError(f"Unknown key: {name}. Available keys: {sorted(KEYS.keys())}")
221 return KEYS[name]
222
223
224 def key_sequence(*keys: str) -> str:
225 """
226 Build a sequence of multiple keys.
227
228 Args:
229 *keys: Key names to combine
230
231 Returns:
232 Combined escape sequence string
233
234 Example:
235 key_sequence("C-a", "C-k") # Move to beginning, kill to end
236 """
237 return "".join(get_key(k) for k in keys)