:tulip: Vim plugin that shows keybindings in popup
vim-which-key
引言
vim-which-key是emacs-which-key在Vim中的实现,它会在弹出窗口中显示可用的键绑定。
emacs-which-key最初是对guide-key的重写,同样地,vim-which-key很大程度上基于vim-leader-guide进行了大量修改。自那时以来,vim-which-key的功能有了很大的发展。
(注:截图中的Vim配置来自space-vim)
优点
- 更好的UI,支持Vim的
popup和Neovim的floating_win。 - 显示所有以特定前缀(如
<leader>,<localleader>等)开头的映射。 - 对每个输入即时响应。
- 每次调用时动态更新。
- 定义分组名称和任意描述。
安装
插件管理器
假设您使用的是vim-plug:
Plug 'liuchengxu/vim-which-key'
" 需要时按需懒加载
Plug 'liuchengxu/vim-which-key', { 'on': ['WhichKey', 'WhichKey!'] }
" 使用按需加载功能注册描述时,
" 可使用autocmd钩子调用which_key#register(),例如,为空格键注册:
" autocmd! User vim-which-key call which_key#register('<Space>', 'g:which_key_map')
对于其他插件管理器,请参考其文档了解详细信息。
包管理
Vim 8
$ mkdir -p ~/.vim/pack/git-plugins/start
$ git clone https://github.com/liuchengxu/vim-which-key.git --depth=1 ~/.vim/pack/git-plugins/start/vim-which-key
Neovim
$ mkdir -p ~/.local/share/nvim/site/pack/git-plugins/start
$ git clone https://github.com/liuchengxu/vim-which-key.git --depth=1 ~/.local/share/nvim/site/pack/git-plugins/start/vim-which-key
要求
vim-which-key需要开启选项timeout,请参阅:h timeout。
由于timeout默认已启用,您只需要确保不在.vimrc中设置notimeout。
使用方法
timeoutlen
假设 <SPC> 是您的领袖键,用于触发vim-which-key:
nnoremap <silent> <leader> :WhichKey '<Space>'<CR>
按下领袖键后,如果在timeoutlen内没有进一步按键,则会弹出指南缓冲区。
" 默认timeoutlen为1000毫秒
set timeoutlen=500
在timeoutlen内按其他键,要么完成映射,要么打开一个子菜单。如上图所示,按下 <SPC> 后再按 <b> 将会打开缓冲区菜单。
请注意,无论配置了哪些映射和菜单,原始的领导者映射都将保持不变。键指南只是附加层,只有在超时时间内未完成输入时才会激活。
特殊键
- 使用
<BS>显示上一级映射。
配置
- 对于Neovim,nvim-whichkey-setup.lua为
vim-which-key提供了一个包装器,简化了lua中的配置。 它解决了当映射的命令更复杂时的问题(见#126),并使为localleader映射变得简单。
最小配置
:WhichKey 和 :WhichKeyVisual 是与该插件交互的主要方式。
假设您的leader和localleader键分别是 <Space> 和 ,,即使没有注册描述字典,也会显示所有与 <Space> 和 , 相关的映射。
let g:mapleader = "\<Space>"
let g:maplocalleader = ','
nnoremap <silent> <leader> :<c-u>WhichKey '<Space>'<CR>
nnoremap <silent> <localleader> :<c-u>WhichKey ','<CR>
如果没有描述字典,通常显示的内容不足以作为速查表。下面的部分介绍了如何正确配置。
如果没有描述字典,那么所有映射的右侧会被显示:

需要字典配置来提供分组名称或描述文本:
let g:which_key_map = {}
let g:which_key_map['w'] = {
\ 'name' : '+windows' ,
\ 'w' : ['<C-W>w' , 'other-window'] ,
\ 'd' : ['<C-W>c' , 'delete-window'] ,
\ ... (省略) ...
\ }
call which_key#register('<Space>', "g:which_key_map")

如果您希望隐藏菜单中的映射,请将其描述设置为'which_key_ignore'。例如,可以隐藏由领袖数字[1-9]表示的窗口交换映射。如下所示的映射将不会出现在菜单中:
nnoremap <leader>1 :1wincmd w<CR>
let g:which_key_map.1 = 'which_key_ignore'
如果您想隐藏非顶级分组中的一系列映射,将name设置为'which_key_ignore'。例如,
nnoremap <leader>_a :echom '_a'<CR>
nnoremap <leader>_b :echom '_b'<CR>
let g:which_key_map['_'] = { 'name': 'which_key_ignore' }
如果您想隐藏所有不在描述字典元素之外的映射,可以使用let g:which_key_ignore_outside_mappings = 1。
示例
您可以为每个前缀配置一个字典,以便显示更易读。为了使指南弹出,首先为前缀注册描述字典。假设Space是您的领袖键,配置字典为g:which_key_map:
nnoremap <silent> <leader> :<c-u>WhichKey '<Space>'<CR>
vnoremap <silent> <leader> :<c-u>WhichKeyVisual '<Space>'<CR>
call which_key#register('<Space>', "g:which_key_map")
接下来,在g:which_key_map中添加项目:
...
...(此处继续添加更多配置示例)...
...
定义快捷键字典
在 Vim 中配置 which-key 插件来增强快捷键的可发现性与使用效率。
首先,我们创建一个全局快捷键字典 g:which_key_map 用于组织命令结构。
分层菜单构建
-
文件操作 (+file)
- 映射
<leader>fs到保存文件 (save-file) - 映射
<leader>fd打开 Vim 配置文件 (open-vimrc)
- 映射
-
打开相关 (+open)
- 使用
<leader>oq和<leader>ol分别打开快速修复和位置列表窗口,并定义对应描述。
- 使用
独立菜单项创建 不依赖于现有映射,直接定义命令及说明,比如缓冲区管理:
- 提供一系列缓冲区操作,包括切换到特定编号的缓冲区、删除缓冲区等,并通过
'?'引入 FZF 来选择缓冲区。
语言服务器协议 (LSP) 功能集成
- 对 LSP 相关功能如格式化、查找引用、重命名等进行绑定,并嵌套 ‘+goto’ 子菜单展示定位相关的命令。
自定义触发命令 除了默认的 leader 键外,可以通过其他键触发指南,例如本地 leader 键,以提供局部上下文的快捷方式访问。
隐藏状态栏
由于 which-key 弹出窗已显示必要信息,可通过自动命令动态调整状态栏的可见性,保持界面清爽。
命令与选项概览
:WhichKey {prefix}用于查看指定前缀下的所有快捷键。:WhichKey! {dict}直接显示特定字典内容的指南。
配置选项允许调整弹窗样式,比如垂直显示、窗口位置、列间距及是否居中。
常见问题解答
- 如何映射特殊键(如退格键)?
- 如何基于文件类型或其他条件设置键绑定?
- 如何映射 Lua 函数作为快捷键?
对特定情况的映射,如 Lua 函数绑定,需利用外部脚本或插件支持,例如通过 nvim-whichkey-setup.lua 实现更灵活的配置,确保所有配置迁移至新系统以启用此高级功能。
致谢
该插件的灵感和技术参考来源于多个项目,特别提到 vim-leader-guide,以及社区中的解决方案,它们共同推动了 Vim 用户体验的提升。