How do I create a mapping that does different things based on context?
Answer
nnoremap <expr> j (v:count == 0 ? 'gj' : 'j')
Explanation
The <expr> map modifier turns a mapping's right-hand side into a Vimscript expression that is evaluated at the time the key is pressed, with its return value used as the actual keystrokes to execute. This enables smart, context-sensitive mappings without writing a full function.
How it works
<expr>is placed between the map command and the lhs key- The rhs is any Vimscript expression that returns a string
- The returned string is fed back to Vim as if you typed those characters
v:countholds the count prefix typed before the key (0 if none)
The classic example remaps j and k to move by display lines (gj/gk) when no count is given, but reverts to logical-line movement when a count is provided (so 5j still jumps 5 real lines, enabling relative line numbers to work correctly):
nnoremap <expr> j (v:count == 0 ? 'gj' : 'j')
nnoremap <expr> k (v:count == 0 ? 'gk' : 'k')
Example
Another common use: a smart <CR> in the command window vs. normal buffers:
nnoremap <expr> <CR> &buftype ==# 'quickfix' ? '<CR>' : 'o<Esc>'
This opens a new line with o in normal buffers, but lets <CR> jump to the quickfix entry in the quickfix window.
Tips
- The expression must return a string (or an empty string
''to do nothing) - Use
mode()inside the expression to branch on the current mode when usingmap(withoutn/i/vprefix) - Combine with
has()orexists()to guard against missing features - For more complex logic, call a function:
nnoremap <expr> <Tab> MyTabKey()