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.

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.

As always, these scripts can get rather complex if you're trying to account for all of Vim's quirks and nuances. For example, you might notice that F8 can really be any key. Here is what I came up with as a more general purpose key handler.

KeyPrinter() will echo what key your pressed based on the dictionary KPDict. Some explanation:

The script sets g:GC_ProcChar and g:GC_ProcEsc and then calls GetChar().

feedkeys(...)[17] is just a cute way to produce an empty string, since 0[17] is an empty string.

In KeyPrinterHandler, there is a loop that gets the full keypress. For example, the F1 key is actually ^[OP, but ^[ is eaten up by our mapping, so we would need to check whether any keypresses have been buffered.

Note the while c isnot 0. In vimscript, strings are in fact 'equal to' 0 (ie 'f'==0 is true) so we need the is comparison here, since c can be a number or a string. ('f' isnot 0 is true)