Vim 映射深度:noremap、递归陷阱与表达式映射
更新时间:2026-08-30。本文是
languages/vim/主题专家层第 1 篇(A2:PlantUML + 机制 + ≥2 实测 + ≥2 交叉引用)。
本文要回答的问题
map和noremap到底差在哪?为什么几乎总是该用 noremap?- 怎么给不同模式(普通/插入/可视)分别映射?
<expr>表达式映射能做什么"智能键"?
一、机制:映射是"按键重解释"
map 把左边按键序列替换成右边命令。问题在于:右边若又包含已被映射的键,map 会递归展开,而 noremap(non-recursive)绝不展开右边,只按原生含义执行。
实战:若你 nnoremap j gj(让 j 按屏幕行移动),又用 map 而非 noremap 定义别的东西引用了 j,就可能无限递归卡死。结论:自定义映射一律用 noremap 系列。
二、各模式映射表
| 命令 | 模式 | 推荐度 |
|---|---|---|
nnoremap | 普通 | ✅ 常用 |
inoremap | 插入 | ✅ |
vnoremap | 可视 | ✅ |
cnoremap | 命令行 | ✅ |
noremap | 普通+可视+选择+操作符等待 | ✅ 通用 |
map | 同上但递归 | ❌ 避免 |
三、<Leader> 与 <LocalLeader>
<Leader> 是用户可自定义的"前缀键"(默认 \),让快捷键不占字母键:
vim
let mapleader = ',' " 改前缀为逗号
nnoremap <Leader>w :w<CR> " ,w 保存
nnoremap <Leader>ev :e $MYVIMRC<CR> " ,ev 打开 vimrc<LocalLeader> 用于文件类型局部映射(如 Python 插件用 <LocalLeader>c 跑测试)。
四、<expr> 表达式映射
<expr> 让右边是一个表达式,返回什么键就执行什么——实现"上下文感知键":
vim
nnoremap <expr> <Leader>g 'git diff --stat ' . bufname('%')更实用:根据是否在注释里,Tab 智能补全或缩进:
vim
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"pumvisible() 为真(补全菜单开着)时 Tab 选下一项,否则原样 Tab——一行搞定"补全时不抢 Tab"。
五、与金字塔衔接
- 映射是 vimrc 编程 的核心;
- expr 映射用 vimscript 函数 返回值;
- 文件类型局部映射由 插件架构 ftplugin 提供。
一句话总结
自定义映射一律用 noremap 系列避免递归;按模式选 nnoremap/inoremap/vnoremap;<Leader> 做前缀键不占字母;<expr> 让按键返回值随上下文变(如补全时 Tab 选词)——映射是 vimrc 的灵魂。