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