Vim 自动补全怎么用?内置命令和 coc.nvim 怎么配置?
Vim 的补全比你想象的好用
很多从 VS Code 转过来的开发者打开 Vim 的第一反应是:补全呢?输入一个函数名的前几个字母,什么都不会发生。其实 Vim 不是没有补全,只是它默认不自动弹出,需要你主动触发。理解这一点,是从"Vim 补全不好用"到"原来可以这样"的关键转折。
基本关键字补全:Ctrl+N 和 Ctrl+P
在插入模式下,输入几个字符后按 Ctrl+N,Vim 会在当前缓冲区、其他打开的缓冲区、以及标签文件中搜索匹配的关键字,弹出一个补全菜单。Ctrl+P 的作用相同,只是列表方向相反——从下往上选。
这两个快捷键是 Vim 补全的起点,也是日常编码中使用频率最高的。补全菜单弹出后,继续按 Ctrl+N/Ctrl+P 可以在候选项之间上下移动,按回车确认选择。
一个容易忽略的细节:complete 选项决定了 Ctrl+N/Ctrl+P 的搜索范围。默认值是 .,w,b,u,t,i,分别代表当前缓冲区、当前窗口的其他缓冲区、卸载的缓冲区、标签、以及包含的文件。你可以通过 :set complete? 查看当前设置,也可以根据需要调整。
Ctrl+X 系列:针对性补全
Ctrl+N/Ctrl+P 是通用关键字补全,而 Ctrl+X 开启的是一个补全模式前缀,后面跟不同的按键触发不同类型的补全。这套组合是 Vim 内置补全的精华所在:
Ctrl+X Ctrl+L— 整行补全。匹配当前文件中已有的完整行,在写重复性代码(比如结构相似的配置项)时特别省事。Ctrl+X Ctrl+N/Ctrl+X Ctrl+P— 当前文件的关键字补全,和前面的 Ctrl+N/Ctrl+P 类似,但限定了搜索范围。Ctrl+X Ctrl+I— 包含文件的关键字补全。搜索#include或import引入的文件中的关键字,写 C 或 Python 时很实用。Ctrl+X Ctrl+]— 标签补全。需要先用 ctags 生成 tags 文件,然后可以补全函数名、结构体成员等。Ctrl+X Ctrl+F— 文件名补全。输入路径时按这个组合,Vim 会列出当前目录下的文件和文件夹,写import或require语句时效率翻倍。Ctrl+X Ctrl+K— 字典补全。从dictionary选项指定的字典文件中匹配单词,主要用在写英文文档或注释时。Ctrl+X Ctrl+V— Vim 命令行补全,日常编码用得少,写 vimrc 时倒是可以派上用场。
在补全菜单弹出后,Ctrl+N 和 Ctrl+P 依然可以用来在候选项之间导航。按 Ctrl+E 可以取消补全回到原始输入,按 Ctrl+Y 则确认当前选中项。
全能补全(Omni Completion):Ctrl+X Ctrl+O
如果说前面那些补全方式是"按字面匹配",那 Omni 补全就是"按语义匹配"。它由 omnifunc 选项指定的函数驱动,能够理解代码结构,给出上下文相关的补全建议。
使用前需要确保 .vimrc 中有这两行:
vimfiletype plugin on
然后在插入模式下按 Ctrl+X Ctrl+O,Vim 会调用当前文件类型对应的补全函数。比如在 HTML 文件中输入 <p cl 后触发 Omni 补全,会出现 class= 这样的属性建议;在 C 文件中输入结构体变量后加 . 或 ->,会列出结构体成员。
Vim 自带了几种语言的 Omni 补全脚本(位于 $VIMRUNTIME/autoload/ 目录下),覆盖 C、HTML/CSS、JavaScript、PHP、Python、Ruby、SQL、XML 等语言。如果当前文件类型没有对应的 omnifunc,可以在 vimrc 中设置一个兜底方案:
vimautocmd FileType * \ if &omnifunc == '' | \ setlocal omnifunc=syntaxcomplete#Complete | \ endif
这样即使没有专门的补全脚本,也能基于语法高亮信息提供基本的补全。
Omni 补全的局限也很明显:它依赖 Vim 脚本实现,对语言的语义理解深度有限,不能做跨文件的类型推断,也不支持复杂的代码分析。这也是第三方补全插件出现的原因。
coc.nvim:把 LSP 补全带入 Vim
coc.nvim 是目前 Vim/Neovim 生态中最主流的补全插件。它的核心思路是利用 Language Server Protocol(LSP),让 Vim 获得和 VS Code 一样的补全能力——包括类型推断、跨文件跳转、函数签名提示等。
安装 coc.nvim
前提条件:Vim 8.0+ 或 Neovim 0.4.4+,以及 Node.js 14+。
以 vim-plug 为例,在 .vimrc 中添加:
vimcall plug#begin('~/.vim/plugged') Plug 'neoclide/coc.nvim', {'branch': 'release'} call plug#end()
然后在 Vim 中执行 :PlugInstall。
安装语言服务扩展
coc.nvim 本身不包含语言支持,需要安装对应语言的扩展,方式和 VS Code 类似:
vim:CocInstall coc-tsserver " JavaScript/TypeScript :CocInstall coc-pyright " Python :CocInstall coc-clangd " C/C++ :CocInstall coc-json " JSON :CocInstall coc-html " HTML :CocInstall coc-css " CSS
安装完成后重新打开文件,输入代码时补全菜单会自动弹出。
常用配置
在 ~/.vim/coc-settings.json(Neovim 用户是 ~/.config/nvim/coc-settings.json)中可以调整补全行为:
json{ "suggest.autoTrigger": "always", "suggest.maxCompleteItemCount": 15, "suggest.noselect": false, "suggest.enablePreselect": true }
其中 suggest.autoTrigger 设为 "always" 后,输入任何字符都会触发补全;设为 "trigger" 则只在特定触发字符后弹出。建议初学者先用 "always",习惯后再根据偏好调整。
按键映射方面,推荐在 .vimrc 中加入 Tab 补全和回车确认:
vim" Tab 触发/切换补全 inoremap <silent><expr> <TAB> \ pumvisible() ? "\<C-n>" : \ <SID>check_back_space() ? "\<TAB>" : \ coc#refresh() inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>" function! s:check_back_space() abort let col = col('.') - 1 return !col || getline('.')[col - 1] =~# '\s' endfunction " 回车确认补全 inoremap <silent><expr> <CR> coc#pum#visible() ? coc#pum#confirm() \: "\<C-g>u\<CR>\<c-r>=coc#on_enter()\<CR>"
这样 Tab 键在补全菜单可见时切换候选项,不可见时正常插入缩进;回车键在有选中项时确认补全,否则正常换行。
YouCompleteMe 和其他方案
YouCompleteMe(YCM)曾是 Vim 补全领域最有名的插件,基于 clang 和 jedi 提供语义补全,补全速度快、准确度高。但它的安装流程出了名地麻烦——需要编译 C++ 核心,加上各种语言的依赖,完整安装可能拉下来 1GB 以上的文件。配置也不直观,.ycm_extra_conf.py 文件的编写劝退了不少人。
在 LSP 生态成熟之后,YCM 的优势逐渐被 coc.nvim 和 Neovim 原生 LSP 客户端(如 nvim-lspconfig + nvim-cmp)替代。如果你是新用户,建议直接从 coc.nvim 起步。如果已经在用 YCM 且没有问题,也没有强求迁移的必要。
对于 Neovim 用户,nvim-cmp 是另一个值得关注的补全框架。它不绑定特定 LSP 客户端,支持多种补全源(LSP、buffer、路径、snippet 等),配合 nvim-lspconfig 使用。配置比 coc.nvim 稍复杂,但灵活度更高,适合喜欢折腾 Lua 配置的人。
让内置补全更好用的几项配置
即使装了 coc.nvim,Vim 内置补全依然有它的价值——在编辑配置文件、写 Markdown、或者打开一个不想装插件的服务器环境时,原生补全依然是最快的工具。以下是几个实用的配置建议:
设置字典补全的词库路径:
vimset dictionary+=/usr/share/dict/words
扩大 Ctrl+N/Ctrl+P 的搜索范围:
vimset complete+=k " 加入字典搜索 set complete+=t " 加入标签搜索
忽略大小写匹配:
vimset ignorecase set infercase " 补全时根据已输入部分自动调整大小写
补全菜单的显示优化:
vimset completeopt=menuone,noinsert,noselect
menuone 确保即使只有一个匹配项也弹出菜单,noinsert 不自动插入文本,noselect 不自动选中第一项,把选择权留给你。
Vim 的补全体系是一条从简单到复杂的渐进路径。从最基础的 Ctrl+N 开始,到 Ctrl+X 系列的针对性补全,再到 Omni 补全的语义理解,最后到 coc.nvim 的完整 LSP 支持——每一层都能解决一部分问题,每一层也都有适用的场景。不需要一步到位装上所有插件,先把 Ctrl+N 和 Ctrl+X Ctrl+F 用起来,你会发现内置补全已经覆盖了日常编码的相当一部分需求。