用了好几年的 YouCompleteMe,体验还是不错的,最爽的一点是能基于 Token 补全在当前 buffer 中作补全,任何语言用 Vim 就有一个“基本可用”的体验,即使写 git commit message 的时候也很好用。
但是最近玩一些神奇的东西,YouCompleteMe 如果没有支持这种语言的话,就没办法做到一个比较好的体验了。
对于一种语言,YouCompleteMe 都要去实现这种语言的支持,才能起作用。每增加一种语言,YouCompleteMe 都要增加一种工作量。不光 YouCompleteMe,像 Emacs,VS Code,都要去实现每一个语言的自动补全。这就形成了很大的浪费:每一种编辑器要去自己实现每一种语言。如果有人写了一个新的语言想要其他编辑器支持,需要一个一个实现编辑器的补全工具。
这就形成了一种交叉关系,假如有三个语言,三个编辑器存在,就需要9个补全的实现。
所以 VS Code 就提出了 “Language Server” (简称 LSP)这种先进的概念。这是一种 Client-Server 架构,每个语言实现自己的 Language Server,每个编辑器去实现自己对接 Language Server 的前端。这样一个语言只需要实现一次,就可以支持所有的编辑器。上面那种情况,只要有3个语言的补全实现+3个编辑器自己的 Language Server 实现就可以了。
其实 YouCompleteMe 也支持 LSP 的,但是配置上看起来资料比较少,像是不是支持的特别好,所以我索性放弃 YCM 了,转向了 vim-lsp。
配置 vim-lsp
这里以 Python 为例,介绍一下配置方法。
首先需要安装 Python 的 Language Server。推荐使用 pipx 安装。
1 |
$ pipx install python-language-server |
然后修改 .vimrc
的配置修改,以下是一个最小化配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Plugin 'prabirshrestha/async.vim' Plugin 'prabirshrestha/asyncomplete.vim' Plugin 'prabirshrestha/vim-lsp' Plugin 'prabirshrestha/asyncomplete-lsp.vim' inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>" inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>" inoremap <expr> <cr> pumvisible() ? "\<C-y>\<cr>" : "\<cr>" if executable('pyls') au User lsp_setup call lsp#register_server({ \ 'name': 'pyls', \ 'cmd': {server_info->['pyls']}, \ 'whitelist': ['python'], \ }) endif |
安装的插件需要4个,下面分别介绍一下这四个是干什么用的。
async.vim
因为补全是支持 Vim 和 Neovim 的,async.vim 封装了两个 Vim Async 相关的接口。
asyncomplete.vim
利用 async.vim 做的补全,相当于一个补全引擎。(到这里是和 lsp 还没有什么事儿,配置的都是补全插件。)
这个补全只是负责补全引擎,具体的补全还是要交给依赖此引擎的实现。比如我们想要对路径进行自动补全,需要先安装这个插件,再安装基于此插件实现的路径补全插件,最后对补全引擎注册这个插件,才能使用。安装路径补全插件和注册的配置如下。
1 2 3 4 5 6 7 8 |
Plugin 'prabirshrestha/asyncomplete-file.vim' au User asyncomplete_setup call asyncomplete#register_source(asyncomplete#sources#file#get_source_options({ \ 'name': 'file', \ 'whitelist': ['*'], \ 'priority': 10, \ 'completor': function('asyncomplete#sources#file#completor') \ })) |
还有很多挺有意思的补全,比如这个 tmux-completion,是基于 tmux 的窗口 buffer 来补全,在 vim 中抄写 tmux 里面的 git hash,URL 啥的挺有帮助。但是我没找到禁用它的方法,导致它会一直弹出来,太烦人了,就没用。
vim-lsp
是一个与 LSP 交互的插件,类似于一个 Vim 的 SDK。
asyncomplete-lsp
将 lsp 中的内容交给 asyncomplete 做补全。
后面的两个配置,一个是设置用 Tab 触发补全(判断补全窗口有没有打开,有打开的话就将tab
键映射为补全选择键。
1 2 3 |
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>" inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>" inoremap <expr> <cr> pumvisible() ? "\<C-y>\<cr>" : "\<cr>" |
然后注册 Python 的 LSP。每一个 LSP 都需要注册。
1 2 3 4 5 6 7 8 |
if executable('pyls') " pip install python-language-server au User lsp_setup call lsp#register_server({ \ 'name': 'pyls', \ 'cmd': {server_info->['pyls']}, \ 'whitelist': ['python'], \ }) endif |
效果如下:
配置好这些插件之后,再配置其他语言的 LSP 就简单多了。分成两步:
- 安装该语言的 LSP,这里有一个列表;
- 在 Vim 中注册该 LSP;
以 Bash 为例,我们首先按照仓库的介绍,安装该语言的 LSP:
1 |
$ npm i -g bash-language-server |
然后在 .vimrc
中用以下配置注册:
1 2 3 4 5 6 7 |
if executable('bash-language-server') au User lsp_setup call lsp#register_server({ \ 'name': 'bash-language-server', \ 'cmd': {server_info->[&shell, &shellcmdflag, 'bash-language-server start']}, \ 'whitelist': ['sh'], \ }) endif |
换成其他语言的话,只要将 cmd
换成 LSP 的启动命令,然后通过 whitelist
或者 blacklist
设置生效的文件类型就好了。
Snip 的支持
Language Server 支持补全一段代码模板,比如写 Elixir 的时候自动补全 defmodule
。但是 vim-lsp
本身是不支持的,snip 在 vim-lsp 中会补全成这个样子:
将这个片段正确的显示在 vim 中,并且支持跳转,需要通过插件来解析 Language Server 返回的片段。
这里又涉及到 N 多插件。首先你需要一个代码片段引擎,负责在 Vim 中生成片段,在片段中需要填写的字段中进行来回跳转,补全。之前在博客中介绍过 Ultisnips ,这里就以它为例。
除了 SirVer/ultisnips 本身,还要安装两个插件来将 LSP 中的 snip 补全到 vim 中来。总共需要安装的插件如下:
1 2 3 |
Plugin 'SirVer/ultisnips' Plugin 'thomasfaingnaert/vim-lsp-snippets' Plugin 'thomasfaingnaert/vim-lsp-ultisnips' |
然后有一个小坑需要注意,Snip 必须确认选中才能展开,也就是菜单中将焦点移动到这个 Snip 上去之后,需要按 Ctrl
+ y
来展开。
但是这个快捷键可以更改,比如我换成了 Ctrl
+ o
1 |
inoremap <expr> <C-o> pumvisible() ? "\<C-y>" : "\<C-o>" |
前段时间也去了解过各种代码不全
被人安利了一个叫 tabNine的东西
这家伙不全的思路和其他插件完全不一样,
他是用ml来预测,完全没有啥语法树啥的
所以速度出奇的快,推荐可以试试
另外催更捕蛇者说
Cool 我也听说过这个,补全上用大量代码做训练,效果应该不错。
但是语法树应该会更准确,LSP 还能提供跳转到定义,跳转到声明,引用列表,冲命令变量等功能。功能列表
用机器学习的话相当于补全工具并不知道你写的语法,甚至不知道你在写啥语言,只是基于数据帮你生成代码。我觉得实用价值不大。
PS 捕蛇者说第10期录完了,laike9m在剪辑。
coc.nvim 有试过么..目前在用这个,也挺好使的。不知道跟 vim-lsp 怎么样。
看起来不错,就是配置有点复杂…… 有空看看吧。先用一段时间 vim-lsp 试试
这个vim-lsp不稳定啊,对于python来说
,比如我输入import ,有时候会补全这个,而有时候会卡死,后边的库名,则经常出现没有出现补全提示的情况,我现在是jedi-vim/ale/gtags来实现自动补全/改名/跳转到定义/查找引用的。或许是因为我用的是win10?
用了一段时间发现确实会有些问题,没有出现补全是因为异步吧。卡死的情况应该是 asyncomplete 的问题,我补全js的时候遇到过。
coc-nvim 还是很推荐的,确实如其作者大大所说,给人以vscode一般的体验,并且非常的快,且插件化,国人精品。
让我最惊喜的是支持跨文件的重构(需要LSP支持),
另外衍生的插件已经取代了我很多老的插件了,
如
内置的CocList 提供一些模糊查找路径,大纲,符号表等 取代了 fzf等同类插件;
coc-explorer 取代了 NERDTree,defx;
coc-pairs 取代了 autopairs;
coc-smartf 取代了 vim-easymotion;
coc-highlight 取代了 vim-cursorline,并提供了颜色板功能(不知道怎么描述,就是可以像ide一样在描述颜色的地方弹出调色窗口
coc-java/ccls/python/rls 取代了那堆各有缺点的补全插件,
coc-pyright 提供 python 静态类型检查,
……
配置也没那么复杂,引入新的配置文件 全局/局部coc-settings.json,就像vscode那样,安装coc-json后提供完整的设置模糊补全,
而这些插件只需要在ex中 :CocInstall 插件名 即可
你甚至可以安装 coc-marketplace 搜索安装更多插件
好用到哭
最后感谢你在捕蛇者说中的分享和开源的 iredis,对初学redis很有帮助
谢谢恢复,你这么说我一定要去试一下了。iredis 最近开发有点慢,有点忙,希望尽快完成支持所有的命令
现在是2023年,YCM对LSP的支持已大大改善,凡是clangd可用的特性,YCM也能用,而且不必进行很麻烦的配置,使用compile_commands.json即可,不过也有可能是我用得比较简单。感兴趣的话可看我这篇文章的结尾部分:
https://foofoodamon.github.io/忙里偷闲打磨代码编辑环境.html
感谢推荐!不过看起来需要配置的内容有点多哦。