How do I create a Vim mapping where the keys it produces are computed dynamically at runtime?
Answer
:nnoremap <expr> {key} {expression}
Explanation
The <expr> argument to any map command (:nmap, :inoremap, etc.) tells Vim to evaluate the right-hand side as a Vimscript expression each time the mapping is triggered, rather than using it as a literal key sequence. This enables mappings that change behavior based on context.
How it works
<expr>must appear before the key and expression in the:mapcommand- The expression is evaluated as Vimscript and must return a string of keys to execute
- The returned string is used exactly as if you had typed those keys — it can include mode-switching characters, function calls, or conditionals
Example
A smarter j that moves by display lines when the line is wrapped, or by real lines otherwise:
:nnoremap <expr> j (v:count == 0 ? 'gj' : 'j')
:nnoremap <expr> k (v:count == 0 ? 'gk' : 'k')
With no count prefix, j maps to gj (moves by visible line). With a count like 5j, it falls back to real-line j.
A mapping that inserts different text based on the current filetype:
:inoremap <expr> <Tab> (&filetype == 'python') ? ' ' : '\t'
Tips
- Use
<expr>with<silent>to suppress command-line echo::nnoremap <silent> <expr> ... - The expression is not executed as an Ex command — return a key sequence string, not a command
- Access Vim state freely:
v:count,&option,mode(),col('.'), etc. - This is the clean alternative to using
has('patch')guards orifstatements around multiple mappings