Proposed tip Please edit this page to improve it, or add your comments below (do not use the discussion page).
A Vim script can wait for user input with getchar()
. But the annoying thing about getchar()
is that it shifts the cursor to the command line while the user waits. This can be disorienting when trying to write a script that captures and processes all keys in input mode, or when one simply wants the cursor to remain in the same spot in normal mode. This tip provides a workaround.
Basic script
The most basic form is something like this:
imap <F8><Esc> <Nop> imap <F8> <c-r>=PrintChar()<cr> function! PrintChar() let chr="" if getchar(1) let chr = getchar() endif call feedkeys("\<F8>") echo chr return '' endfunction
When the user presses F8, the above script simply echos the keypresses until the user presses Escape. Every time PrintChar()
is called, it uses feedkeys()
to reactivate itself, setting up an input loop.
The first line, imap <F8><Esc> <Nop>
serves two functions. First, it allows a way to break the loop. The second is more subtle: it makes the F8 mapping ambiguous, which will cause Vim to pause momentarily to wait for the next keystroke, since it is unsure which mapping the user wants to activate. This is a clever way to slow down the loop while still leaving Vim responsive for user input.
More complex forms
As always, scripts can get complex if you want to handle all the various cases. For example, you might notice that F8 can really be any key. Or, you might wonder how you could incorporate this into your script. Here is what I came up with as a getchar()
replacement.
nmap <plug>xyz<Esc> <Nop> nmap <plug>xyz :call InputLoop()<cr> function! InputLoop() let c = getchar(1) ? getchar() : "" if c != 0 call ProcessChar(c) else call feedkeys("\<plug>xyz") endif return '' endfunction function! GetChar() let g:HandleInput="ProcessChar" call InputLoop() endfunction function! ProcessChar(c) if a:c == char2nr('f') echo "You pressed f" else echo "You didn't press f" endif endfunction
Here, the user calls GetChar()
and waits for some input. The user then responds to that input via ProcessChar()
.
See also
- vim_use mailing list original tip from Andy Wokula; has a 'typewriter mode' that intercepts all user input