How do I read a single keystroke from the user inside a Vimscript mapping or function?
Answer
getchar()
Explanation
getchar() blocks and waits for the user to press a key, returning its numeric character code. Combined with nr2char(), it lets you build interactive mappings that branch on user input — enabling mini-menus, character-driven operators, and dynamic behavior without a plugin framework.
How it works
let char = getchar() " blocks until a key is pressed; returns char code
let str = nr2char(char) " convert to a string character
Key facts:
- Returns a number (e.g.
97fora) - For special keys (arrows, function keys), returns a string like
"\<Up>" - Call inside a mapping with
:callor:execute
Example
A mapping that surrounds the word under the cursor with any pair of quotes or brackets you type next:
nnoremap <Leader>q :call SurroundWord()<CR>
function! SurroundWord()
let open = nr2char(getchar())
let pairs = {'(': ')', '[': ']', '{': '}', '<': '>'}
let close = get(pairs, open, open)
execute 'normal! viw<Esc>a' . close . '\<Esc>bi' . open . '\<Esc>'
endfunction
Press <Leader>q then " to wrap the word in double quotes, or ( to wrap in parentheses.
Tips
- Use
getcharstr()(Vim 8.2+ / Neovim) to get the string directly withoutnr2char() getchar(1)checks if a char is available without blocking (non-blocking peek)- Always guard with
if char ==# "\<Esc>"to cancel gracefully - For Neovim Lua:
vim.fn.getchar()andvim.fn.nr2char()work identically