Mercurial > vim
view src/testdir/keycode_check.vim @ 33713:9aa03e12b2b5 v9.0.2090
patch 9.0.2090: complete_info() skips entries with 'noselect'
Commit: https://github.com/vim/vim/commit/57f9ce1a0977da13e5923214086795ffa2d28ce1
Author: Christian Brabandt <cb@256bit.org>
Date: Sat Nov 4 09:58:14 2023 +0100
patch 9.0.2090: complete_info() skips entries with 'noselect'
Problem: complete_info() skips entries with 'noselect'
Solution: Check, if first entry is at original text state
Unfortunately, Commit daef8c74375141974d61b85199b383017644978c
introduced a regression, that when ':set completeopt+=noselect' is set
and no completion item has been selected yet, it did not fill the
complete_info['items'] list.
This happened, because the current match item did not have the
CP_ORIGINAL_TEXT flag set and then the cp->prev pointer did point to the
original flag item, which caused the following while loop to not being
run but being skipped instead.
So when the 'noselect' is set, only start with to the previous selection
item, if the initial completion item has the CP_ORIGINAL_TEXT flag set,
else use the 2nd previous item instead.
fixes: #13451
closes: #13452
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sat, 04 Nov 2023 10:15:04 +0100 |
parents | dbec60b8c253 |
children |
line wrap: on
line source
vim9script # Script to get various codes that keys send, depending on the protocol used. # # Usage: vim -u NONE -S keycode_check.vim # # Author: Bram Moolenaar # Last Update: 2022 Nov 15 # # The codes are stored in the file "keycode_check.json", so that you can # compare the results of various terminals. # # You can select what protocol to enable: # - None # - modifyOtherKeys level 2 # - kitty keyboard protocol # Change directory to where this script is, so that the json file is found # there. exe 'cd ' .. expand('<sfile>:h') echo 'working in directory: ' .. getcwd() const filename = 'keycode_check.json' # Dictionary of dictionaries with the results in the form: # {'xterm': {protocol: 'none', 'Tab': '09', 'S-Tab': '09'}, # 'xterm2': {protocol: 'mok2', 'Tab': '09', 'S-Tab': '09'}, # 'kitty': {protocol: 'kitty', 'Tab': '09', 'S-Tab': '09'}, # } # The values are in hex form. var keycodes = {} if filereadable(filename) keycodes = readfile(filename)->join()->json_decode() else # Use some dummy entries to try out with keycodes = { 'xterm': {protocol: 'none', 'Tab': '09', 'S-Tab': '09'}, 'kitty': {protocol: 'kitty', 'Tab': '09', 'S-Tab': '1b5b393b3275'}, } endif var orig_keycodes = deepcopy(keycodes) # used to detect something changed # Write the "keycodes" variable in JSON form to "filename". def WriteKeycodes() # If the file already exists move it to become the backup file. if filereadable(filename) if rename(filename, filename .. '~') echoerr $'Renaming {filename} to {filename}~ failed!' return endif endif if writefile([json_encode(keycodes)], filename) != 0 echoerr $'Writing {filename} failed!' endif enddef # The key entries that we want to list, in this order. # The first item is displayed in the prompt, the second is the key in # the keycodes dictionary. var key_entries = [ ['Tab', 'Tab'], ['Shift-Tab', 'S-Tab'], ['Ctrl-Tab', 'C-Tab'], ['Alt-Tab', 'A-Tab'], ['Ctrl-I', 'C-I'], ['Shift-Ctrl-I', 'S-C-I'], ['Esc', 'Esc'], ['Shift-Esc', 'S-Esc'], ['Ctrl-Esc', 'C-Esc'], ['Alt-Esc', 'A-Esc'], ['Space', 'Space'], ['Shift-Space', 'S-Space'], ['Ctrl-Space', 'C-Space'], ['Alt-Space', 'A-Space'], ] # Given a terminal name and a item name, return the text to display. def GetItemDisplay(term: string, item: string): string var val = get(keycodes[term], item, '') # see if we can pretty-print this one var pretty = val if val[0 : 1] == '1b' pretty = 'ESC' var idx = 2 if val[0 : 3] == '1b5b' pretty = 'CSI' idx = 4 endif var digits = false while idx < len(val) var cc = val[idx : idx + 1] var nr = str2nr('0x' .. cc, 16) idx += 2 if nr >= char2nr('0') && nr <= char2nr('9') if !digits pretty ..= ' ' endif digits = true pretty ..= cc[1] else if nr == char2nr(';') && digits # don't use space between semicolon and digits to keep it short pretty ..= ';' else digits = false if nr >= char2nr(' ') && nr <= char2nr('~') # printable character pretty ..= ' ' .. printf('%c', nr) else # non-printable, use hex code pretty = val break endif endif endif endwhile endif return pretty enddef # Action: list the information in "keycodes" in a more or less nice way. def ActionList() var terms = keys(keycodes) if len(terms) == 0 echo 'No terminal results yet' return endif sort(terms) var items = ['protocol', 'version', 'kitty', 'modkeys'] + key_entries->copy()->map((_, v) => v[1]) # For each terminal compute the needed width, add two. # You may need to increase the terminal width to avoid wrapping. var widths = [] for [idx, term] in items(terms) widths[idx] = len(term) + 2 endfor for item in items for [idx, term] in items(terms) var l = len(GetItemDisplay(term, item)) if widths[idx] < l + 2 widths[idx] = l + 2 endif endfor endfor # Use one column of width 10 for the item name. echo "\n" echon ' ' for [idx, term] in items(terms) echon printf('%-' .. widths[idx] .. 's', term) endfor echo "\n" for item in items echon printf('%8s ', item) for [idx, term] in items(terms) echon printf('%-' .. widths[idx] .. 's', GetItemDisplay(term, item)) endfor echo '' endfor echo "\n" enddef # Convert the literal string after "raw key input" into hex form. def Literal2hex(code: string): string var hex = '' for i in range(len(code)) hex ..= printf('%02x', char2nr(code[i])) endfor return hex enddef def GetTermName(): string var name = input('Enter the name of the terminal: ') return name enddef # Gather key codes for terminal "name". def DoTerm(name: string) var proto = inputlist([$'What protocol to enable for {name}:', '1. None', '2. modifyOtherKeys level 2', '3. kitty', ]) echo "\n" &t_TE = "\<Esc>[>4;m" var proto_name = 'unknown' if proto == 1 # Request the XTQMODKEYS value and request the kitty keyboard protocol status. &t_TI = "\<Esc>[?4m" .. "\<Esc>[?u" proto_name = 'none' elseif proto == 2 # Enable modifyOtherKeys level 2 and request the XTQMODKEYS value. &t_TI = "\<Esc>[>4;2m" .. "\<Esc>[?4m" proto_name = 'mok2' elseif proto == 3 # Enable Kitty keyboard protocol and request the status. &t_TI = "\<Esc>[>1u" .. "\<Esc>[?u" proto_name = 'kitty' else echoerr 'invalid protocol choice' return endif # Append the request for the version response, this is used to check we have # the results. &t_TI ..= "\<Esc>[>c" # Pattern that matches the line with the version response. const version_pattern = "\<Esc>\\[>\\d\\+;\\d\\+;\\d*c" # Pattern that matches the XTQMODKEYS response: # CSI > 4;Pv m # where Pv indicates the modifyOtherKeys level const modkeys_pattern = "\<Esc>\\[>4;\\dm" # Pattern that matches the line with the status. Currently what terminals # return for the Kitty keyboard protocol. const kitty_status_pattern = "\<Esc>\\[?\\d\\+u" ch_logfile('keylog', 'w') # executing a dummy shell command will output t_TI !echo >/dev/null # Wait until the log file has the version response. var startTime = reltime() var seenVersion = false while !seenVersion var log = readfile('keylog') if len(log) > 2 for line in log if line =~ 'raw key input' var code = substitute(line, '.*raw key input: "\([^"]*\).*', '\1', '') if code =~ version_pattern seenVersion = true echo 'Found the version response' break endif endif endfor endif if reltime(startTime)->reltimefloat() > 3 # break out after three seconds break endif endwhile echo 'seenVersion: ' seenVersion # Prepare the terminal entry, set protocol and clear status and version. if !has_key(keycodes, name) keycodes[name] = {} endif keycodes[name]['protocol'] = proto_name keycodes[name]['version'] = '' keycodes[name]['kitty'] = '' keycodes[name]['modkeys'] = '' # Check the log file for a status and the version response ch_logfile('', '') var log = readfile('keylog') delete('keylog') for line in log if line =~ 'raw key input' var code = substitute(line, '.*raw key input: "\([^"]*\).*', '\1', '') # Check for the XTQMODKEYS response. if code =~ modkeys_pattern var modkeys = substitute(code, '.*\(' .. modkeys_pattern .. '\).*', '\1', '') # We could get the level out of the response, but showing the response # itself provides more information. # modkeys = substitute(modkeys, '.*4;\(\d\)m', '\1', '') if keycodes[name]['modkeys'] != '' echomsg 'Another modkeys found after ' .. keycodes[name]['modkeys'] endif keycodes[name]['modkeys'] = modkeys endif # Check for kitty keyboard protocol status if code =~ kitty_status_pattern var status = substitute(code, '.*\(' .. kitty_status_pattern .. '\).*', '\1', '') # use the response itself as the status status = Literal2hex(status) if keycodes[name]['kitty'] != '' echomsg 'Another status found after ' .. keycodes[name]['kitty'] endif keycodes[name]['kitty'] = status endif if code =~ version_pattern var version = substitute(code, '.*\(' .. version_pattern .. '\).*', '\1', '') keycodes[name]['version'] = Literal2hex(version) break endif endif endfor echo "For Alt to work you may need to press the Windows/Super key as well" echo "When a key press doesn't get to Vim (e.g. when using Alt) press x" # The log of ignored typeahead is left around for debugging, start with an # empty file here. delete('keylog-ignore') for entry in key_entries # Consume any typeahead. Wait a bit for any responses to arrive. ch_logfile('keylog-ignore', 'a') while 1 sleep 100m if getchar(1) == 0 break endif while getchar(1) != 0 getchar() endwhile endwhile ch_logfile('', '') ch_logfile('keylog', 'w') echo $'Press the {entry[0]} key (q to quit):' var r = getcharstr() ch_logfile('', '') if r == 'q' break endif log = readfile('keylog') delete('keylog') if len(log) < 2 echoerr 'failed to read result' return endif var done = false for line in log if line =~ 'raw key input' var code = substitute(line, '.*raw key input: "\([^"]*\).*', '\1', '') # Remove any version termresponse code = substitute(code, version_pattern, '', 'g') # Remove any XTGETTCAP replies. const cappat = "\<Esc>P[01]+\\k\\+=\\x*\<Esc>\\\\" code = substitute(code, cappat, '', 'g') # Remove any kitty status reply code = substitute(code, kitty_status_pattern, '', 'g') if code == '' continue endif # Convert the literal bytes into hex. If 'x' was pressed then clear # the entry. var hex = '' if code != 'x' hex = Literal2hex(code) endif keycodes[name][entry[1]] = hex done = true break endif endfor if !done echo 'Code not found in log' endif endfor enddef # Action: Add key codes for a new terminal. def ActionAdd() var name = input('Enter name of the terminal: ') echo "\n" if index(keys(keycodes), name) >= 0 echoerr $'Terminal {name} already exists' return endif DoTerm(name) enddef # Action: Replace key codes for an already known terminal. def ActionReplace() var terms = keys(keycodes) if len(terms) == 0 echo 'No terminal results yet' return endif var choice = inputlist(['Select:'] + terms->copy()->map((idx, arg) => (idx + 1) .. ': ' .. arg)) echo "\n" if choice > 0 && choice <= len(terms) DoTerm(terms[choice - 1]) else echo 'invalid index' endif enddef # Action: Clear key codes for an already known terminal. def ActionClear() var terms = keys(keycodes) if len(terms) == 0 echo 'No terminal results yet' return endif var choice = inputlist(['Select:'] + terms->copy()->map((idx, arg) => (idx + 1) .. ': ' .. arg)) echo "\n" if choice > 0 && choice <= len(terms) remove(keycodes, terms[choice - 1]) else echo 'invalid index' endif enddef # Action: Quit, possibly after saving the results first. def ActionQuit() # If nothing was changed just quit if keycodes == orig_keycodes quit endif while true var res = input("Save the changed key codes (y/n)? ") if res == 'n' quit endif if res == 'y' WriteKeycodes() quit endif echo 'invalid reply' endwhile enddef # The main loop while true var action = inputlist(['Select operation:', '1. List results', '2. Add results for a new terminal', '3. Replace results', '4. Clear results', '5. Quit', ]) echo "\n" if action == 1 ActionList() elseif action == 2 ActionAdd() elseif action == 3 ActionReplace() elseif action == 4 ActionClear() elseif action == 5 ActionQuit() endif endwhile