Mercurial > vim
comparison src/testdir/keycode_check.vim @ 31109:17e171cf2cca v9.0.0889
patch 9.0.0889: keycode check script has a few flaws
Commit: https://github.com/vim/vim/commit/8303035d670a50e39a0c099f159ce450d6e1a14e
Author: Bram Moolenaar <Bram@vim.org>
Date: Wed Nov 16 16:08:30 2022 +0000
patch 9.0.0889: keycode check script has a few flaws
Problem: Keycode check script has a few flaws.
Solution: Sort on terminal name. Ignore XTGETTCAP responses. Check for
version and status response. Update entries.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Wed, 16 Nov 2022 17:15:04 +0100 |
parents | 46d1a434784b |
children | 548241980a27 |
comparison
equal
deleted
inserted
replaced
31108:69fabe8204f8 | 31109:17e171cf2cca |
---|---|
1 vim9script | 1 vim9script |
2 | 2 |
3 # Script to get various codes that keys send, depending on the protocol used. | 3 # Script to get various codes that keys send, depending on the protocol used. |
4 # | 4 # |
5 # Usage: vim -u keycode_check.vim | 5 # Usage: vim -u NONE -S keycode_check.vim |
6 # | 6 # |
7 # Author: Bram Moolenaar | 7 # Author: Bram Moolenaar |
8 # Last Update: 2022 Nov 15 | 8 # Last Update: 2022 Nov 15 |
9 # | 9 # |
10 # The codes are stored in the file "keycode_check.json", so that you can | 10 # The codes are stored in the file "keycode_check.json", so that you can |
74 ['Shift-Space', 'S-Space'], | 74 ['Shift-Space', 'S-Space'], |
75 ['Ctrl-Space', 'C-Space'], | 75 ['Ctrl-Space', 'C-Space'], |
76 ['Alt-Space', 'A-Space'], | 76 ['Alt-Space', 'A-Space'], |
77 ] | 77 ] |
78 | 78 |
79 # Given a terminal name and a item name, return the text to display. | |
80 def GetItemDisplay(term: string, item: string): string | |
81 var val = get(keycodes[term], item, '') | |
82 | |
83 # see if we can pretty-print this one | |
84 var pretty = val | |
85 if val[0 : 1] == '1b' | |
86 pretty = 'ESC' | |
87 var idx = 2 | |
88 | |
89 if val[0 : 3] == '1b5b' | |
90 pretty = 'CSI' | |
91 idx = 4 | |
92 endif | |
93 | |
94 var digits = false | |
95 while idx < len(val) | |
96 var cc = val[idx : idx + 1] | |
97 var nr = str2nr('0x' .. cc, 16) | |
98 idx += 2 | |
99 if nr >= char2nr('0') && nr <= char2nr('9') | |
100 if !digits | |
101 pretty ..= ' ' | |
102 endif | |
103 digits = true | |
104 pretty ..= cc[1] | |
105 else | |
106 if nr == char2nr(';') && digits | |
107 # don't use space between semicolon and digits to keep it short | |
108 pretty ..= ';' | |
109 else | |
110 digits = false | |
111 if nr >= char2nr(' ') && nr <= char2nr('~') | |
112 # printable character | |
113 pretty ..= ' ' .. printf('%c', nr) | |
114 else | |
115 # non-printable, use hex code | |
116 pretty = val | |
117 break | |
118 endif | |
119 endif | |
120 endif | |
121 endwhile | |
122 endif | |
123 | |
124 return pretty | |
125 enddef | |
126 | |
79 | 127 |
80 # Action: list the information in "keycodes" in a more or less nice way. | 128 # Action: list the information in "keycodes" in a more or less nice way. |
81 def ActionList() | 129 def ActionList() |
82 var terms = keys(keycodes) | 130 var terms = keys(keycodes) |
83 if len(terms) == 0 | 131 if len(terms) == 0 |
84 echo 'No terminal results yet' | 132 echo 'No terminal results yet' |
85 return | 133 return |
86 endif | 134 endif |
87 | 135 sort(terms) |
88 # Use one column of width 10 for the item name, then columns of 20 | 136 |
89 # characters to fit most codes. You will need to increase the terminal | 137 var items = ['protocol', 'version', 'status'] |
90 # width to avoid wrapping. | 138 + key_entries->copy()->map((_, v) => v[1]) |
91 echon printf(' ') | 139 |
92 for term in terms | 140 # For each terminal compute the needed width, add two. |
93 echon printf('%-20s', term) | 141 # You may need to increase the terminal width to avoid wrapping. |
94 endfor | 142 var widths = [] |
95 echo "\n" | 143 for [idx, term] in items(terms) |
96 | 144 widths[idx] = len(term) + 2 |
97 var items = ['protocol'] + key_entries->copy()->map((_, v) => v[1]) | 145 endfor |
146 | |
147 for item in items | |
148 for [idx, term] in items(terms) | |
149 var l = len(GetItemDisplay(term, item)) | |
150 if widths[idx] < l + 2 | |
151 widths[idx] = l + 2 | |
152 endif | |
153 endfor | |
154 endfor | |
155 | |
156 # Use one column of width 10 for the item name. | |
157 echo "\n" | |
158 echon ' ' | |
159 for [idx, term] in items(terms) | |
160 echon printf('%-' .. widths[idx] .. 's', term) | |
161 endfor | |
162 echo "\n" | |
98 | 163 |
99 for item in items | 164 for item in items |
100 echon printf('%8s ', item) | 165 echon printf('%8s ', item) |
101 for term in terms | 166 for [idx, term] in items(terms) |
102 var val = get(keycodes[term], item, '') | 167 echon printf('%-' .. widths[idx] .. 's', GetItemDisplay(term, item)) |
103 | |
104 # see if we can pretty-print this one | |
105 var pretty = val | |
106 if val[0 : 1] == '1b' | |
107 pretty = 'ESC' | |
108 var idx = 2 | |
109 | |
110 if val[0 : 3] == '1b5b' | |
111 pretty = 'CSI' | |
112 idx = 4 | |
113 endif | |
114 | |
115 var digits = false | |
116 while idx < len(val) | |
117 var cc = val[idx : idx + 1] | |
118 var nr = str2nr('0x' .. cc, 16) | |
119 idx += 2 | |
120 if nr >= char2nr('0') && nr <= char2nr('9') | |
121 if !digits | |
122 pretty ..= ' ' | |
123 endif | |
124 digits = true | |
125 pretty ..= cc[1] | |
126 else | |
127 digits = false | |
128 if nr >= char2nr(' ') && nr <= char2nr('~') | |
129 # printable character | |
130 pretty ..= ' ' .. printf('%c', nr) | |
131 else | |
132 # non-printable, use hex code | |
133 pretty = val | |
134 break | |
135 endif | |
136 endif | |
137 endwhile | |
138 endif | |
139 | |
140 echon printf('%-20s', pretty) | |
141 endfor | 168 endfor |
142 echo '' | 169 echo '' |
143 endfor | 170 endfor |
144 echo "\n" | 171 echo "\n" |
172 enddef | |
173 | |
174 # Convert the literal string after "raw key input" into hex form. | |
175 def Literal2hex(code: string): string | |
176 var hex = '' | |
177 for i in range(len(code)) | |
178 hex ..= printf('%02x', char2nr(code[i])) | |
179 endfor | |
180 return hex | |
145 enddef | 181 enddef |
146 | 182 |
147 def GetTermName(): string | 183 def GetTermName(): string |
148 var name = input('Enter the name of the terminal: ') | 184 var name = input('Enter the name of the terminal: ') |
149 return name | 185 return name |
160 &t_TE = "\<Esc>[>4;m" | 196 &t_TE = "\<Esc>[>4;m" |
161 var proto_name = 'none' | 197 var proto_name = 'none' |
162 if proto == 1 | 198 if proto == 1 |
163 &t_TI = "" | 199 &t_TI = "" |
164 elseif proto == 2 | 200 elseif proto == 2 |
201 # Enable modifyOtherKeys level 2 - no status is reported | |
165 &t_TI = "\<Esc>[>4;2m" | 202 &t_TI = "\<Esc>[>4;2m" |
166 proto_name = 'mok2' | 203 proto_name = 'mok2' |
167 elseif proto == 3 | 204 elseif proto == 3 |
168 &t_TI = "\<Esc>[>1u" | 205 # Enable Kitty keyboard protocol and request the status |
206 &t_TI = "\<Esc>[>1u" .. "\<Esc>[?u" | |
169 proto_name = 'kitty' | 207 proto_name = 'kitty' |
170 else | 208 else |
171 echoerr 'invalid protocol choice' | 209 echoerr 'invalid protocol choice' |
172 return | 210 return |
173 endif | 211 endif |
174 | 212 |
213 # Append the request for the version response, this is used to check we have | |
214 # the results. | |
215 &t_TI ..= "\<Esc>[>c" | |
216 | |
217 # Pattern that matches the line with the version response. | |
218 const version_pattern = "\<Esc>\\[>\\d\\+;\\d\\+;\\d*c" | |
219 | |
220 # Pattern that matches the line with the status. Currently what terminals | |
221 # return for the Kitty keyboard protocol. | |
222 const status_pattern = "\<Esc>\\[?\\d\\+u" | |
223 | |
224 ch_logfile('keylog', 'w') | |
225 | |
175 # executing a dummy shell command will output t_TI | 226 # executing a dummy shell command will output t_TI |
176 !echo >/dev/null | 227 !echo >/dev/null |
177 | 228 |
229 # Wait until the log file has the version response. | |
230 var startTime = reltime() | |
231 var seenVersion = false | |
232 while !seenVersion | |
233 var log = readfile('keylog') | |
234 if len(log) > 2 | |
235 for line in log | |
236 if line =~ 'raw key input' | |
237 var code = substitute(line, '.*raw key input: "\([^"]*\).*', '\1', '') | |
238 if code =~ version_pattern | |
239 seenVersion = true | |
240 echo 'Found the version response' | |
241 break | |
242 endif | |
243 endif | |
244 endfor | |
245 endif | |
246 if reltime(startTime)->reltimefloat() > 3 | |
247 break | |
248 endif | |
249 endwhile | |
250 | |
251 echo 'seenVersion: ' seenVersion | |
252 | |
253 # Prepare the terminal entry, set protocol and clear status and version. | |
178 if !has_key(keycodes, name) | 254 if !has_key(keycodes, name) |
179 keycodes[name] = {} | 255 keycodes[name] = {} |
180 endif | 256 endif |
181 keycodes[name]['protocol'] = proto_name | 257 keycodes[name]['protocol'] = proto_name |
182 | 258 keycodes[name]['version'] = '' |
183 echo "When a key press doesn't get to Vim (e.g. when using Alt) press Space" | 259 keycodes[name]['status'] = '' |
260 | |
261 # Check the log file for a status and the version response | |
262 ch_logfile('', '') | |
263 var log = readfile('keylog') | |
264 delete('keylog') | |
265 for line in log | |
266 if line =~ 'raw key input' | |
267 var code = substitute(line, '.*raw key input: "\([^"]*\).*', '\1', '') | |
268 # Check for kitty keyboard protocol status | |
269 if code =~ status_pattern | |
270 var status = substitute(code, '.*\(' .. status_pattern .. '\).*', '\1', '') | |
271 keycodes[name]['status'] = Literal2hex(status) | |
272 endif | |
273 | |
274 if code =~ version_pattern | |
275 var version = substitute(code, '.*\(' .. version_pattern .. '\).*', '\1', '') | |
276 keycodes[name]['version'] = Literal2hex(version) | |
277 break | |
278 endif | |
279 endif | |
280 endfor | |
281 | |
282 echo "For Alt to work you may need to press the Windows/Super key as well" | |
283 echo "When a key press doesn't get to Vim (e.g. when using Alt) press x" | |
184 | 284 |
185 for entry in key_entries | 285 for entry in key_entries |
286 # Consume any typeahead. Wait a bit for any responses to arrive. | |
287 sleep 100m | |
288 while getchar(1) | |
289 getchar() | |
290 sleep 100m | |
291 endwhile | |
292 | |
186 ch_logfile('keylog', 'w') | 293 ch_logfile('keylog', 'w') |
187 echo $'Press the {entry[0]} key (q to quit):' | 294 echo $'Press the {entry[0]} key (q to quit):' |
188 var r = getcharstr() | 295 var r = getcharstr() |
189 ch_logfile('', '') | 296 ch_logfile('', '') |
190 if r == 'q' | 297 if r == 'q' |
191 break | 298 break |
192 endif | 299 endif |
193 var log = readfile('keylog') | 300 log = readfile('keylog') |
194 delete('keylog') | 301 if entry[1] == 'Tab' |
302 # keep a copy | |
303 rename('keylog', 'keylog-tab') | |
304 else | |
305 delete('keylog') | |
306 endif | |
195 if len(log) < 2 | 307 if len(log) < 2 |
196 echoerr 'failed to read result' | 308 echoerr 'failed to read result' |
197 return | 309 return |
198 endif | 310 endif |
199 var done = false | 311 var done = false |
200 for line in log | 312 for line in log |
201 if line =~ 'raw key input' | 313 if line =~ 'raw key input' |
202 var code = substitute(line, '.*raw key input: "\([^"]*\).*', '\1', '') | 314 var code = substitute(line, '.*raw key input: "\([^"]*\).*', '\1', '') |
203 | 315 |
204 # convert the literal bytes into hex | 316 # Remove any version termresponse |
317 code = substitute(code, version_pattern, '', 'g') | |
318 | |
319 # Remove any XTGETTCAP replies. | |
320 const cappat = "\<Esc>P[01]+\\k\\+=\\x*\<Esc>\\\\" | |
321 code = substitute(code, cappat, '', 'g') | |
322 | |
323 # Remove any kitty status reply | |
324 code = substitute(code, status_pattern, '', 'g') | |
325 if code == '' | |
326 continue | |
327 endif | |
328 | |
329 # Convert the literal bytes into hex. If 'x' was pressed then clear | |
330 # the entry. | |
205 var hex = '' | 331 var hex = '' |
206 for i in range(len(code)) | 332 if code != 'x' |
207 hex ..= printf('%02x', char2nr(code[i])) | 333 hex = Literal2hex(code) |
208 endfor | 334 endif |
335 | |
209 keycodes[name][entry[1]] = hex | 336 keycodes[name][entry[1]] = hex |
210 done = true | 337 done = true |
211 break | 338 break |
212 endif | 339 endif |
213 endfor | 340 endfor |
239 | 366 |
240 var choice = inputlist(['Select:'] + terms->copy()->map((idx, arg) => (idx + 1) .. ': ' .. arg)) | 367 var choice = inputlist(['Select:'] + terms->copy()->map((idx, arg) => (idx + 1) .. ': ' .. arg)) |
241 echo "\n" | 368 echo "\n" |
242 if choice > 0 && choice <= len(terms) | 369 if choice > 0 && choice <= len(terms) |
243 DoTerm(terms[choice - 1]) | 370 DoTerm(terms[choice - 1]) |
244 endif | 371 else |
245 echo 'invalid index' | 372 echo 'invalid index' |
373 endif | |
374 enddef | |
375 | |
376 # Action: Clear key codes for an already known terminal. | |
377 def ActionClear() | |
378 var terms = keys(keycodes) | |
379 if len(terms) == 0 | |
380 echo 'No terminal results yet' | |
381 return | |
382 endif | |
383 | |
384 var choice = inputlist(['Select:'] + terms->copy()->map((idx, arg) => (idx + 1) .. ': ' .. arg)) | |
385 echo "\n" | |
386 if choice > 0 && choice <= len(terms) | |
387 remove(keycodes, terms[choice - 1]) | |
388 else | |
389 echo 'invalid index' | |
390 endif | |
246 enddef | 391 enddef |
247 | 392 |
248 # Action: Quit, possibly after saving the results first. | 393 # Action: Quit, possibly after saving the results first. |
249 def ActionQuit() | 394 def ActionQuit() |
250 # If nothing was changed just quit | 395 # If nothing was changed just quit |
269 while true | 414 while true |
270 var action = inputlist(['Select operation:', | 415 var action = inputlist(['Select operation:', |
271 '1. List results', | 416 '1. List results', |
272 '2. Add results for a new terminal', | 417 '2. Add results for a new terminal', |
273 '3. Replace results', | 418 '3. Replace results', |
274 '4. Quit', | 419 '4. Clear results', |
420 '5. Quit', | |
275 ]) | 421 ]) |
276 echo "\n" | 422 echo "\n" |
277 if action == 1 | 423 if action == 1 |
278 ActionList() | 424 ActionList() |
279 elseif action == 2 | 425 elseif action == 2 |
280 ActionAdd() | 426 ActionAdd() |
281 elseif action == 3 | 427 elseif action == 3 |
282 ActionReplace() | 428 ActionReplace() |
283 elseif action == 4 | 429 elseif action == 4 |
430 ActionClear() | |
431 elseif action == 5 | |
284 ActionQuit() | 432 ActionQuit() |
285 endif | 433 endif |
286 endwhile | 434 endwhile |