为了体验 LSP 的效果,最近试用了下 LanguageClient-neovim 这个插件(以下简称 LCN )。
总的来说 LSP 的基本功都已实现,但是细节体验上还跟 vscode 存在很大差距,尤其是在补全功能的实现上面。
我给作者提了几次 issue, 但是似乎作者不愿意讨论相关问题,所以记录在此,以供诸位 vim 用户参考、讨论。
LCN 补全中存在的问题
-
不支持
complete的supportSnippet却会在 initialize 中发送{"supportSnippet": true}。LCN 简单的检测了用户是否安装了 snippet 相关插件,如果有安装就声明 supportSnippet 为 true,但是实际上 LCN 并没有任何代码支持对 LSP 服务端返回的 snippet 进行扩展,也没有为 completion 插件声明如何处理 snippet。相关 issue: https://github.com/autozimu/LanguageClient-neovim/issues/379 https://github.com/autozimu/LanguageClient-neovim/pull/410
-
可能错误的补全起始位置判定:
源码使用了 python 的
\w寻找补全单词起始位: https://github.com/autozimu/LanguageClient-neovim/blob/next/rplugin/python3/deoplete/sources/LanguageClientSource.py#L20 但是不同语言类型关键词并不相同,这里使用 vim 中的\k来寻找补全的起始位置应该更合适些。正确的实现应该是在 LSP 服务端返回的TextEdit中的range里面获取补全的起始和终止位置。 -
发送错误的补全起始位置给 LSP server。
LSP 服务端 completion 接受的 position 中的 charactor 字段应该是当前光标所在列,但是 LSP 却会发送当前单词第一个字母所在列,相关代码: https://github.com/autozimu/LanguageClient-neovim/blob/next/rplugin/python3/deoplete/sources/LanguageClientSource.py#L23, 这会导致部分 LSP 服务端位置判定错,例如: https://github.com/chemzqm/wxml-languageserver 需要在补全时判定当前光标后面是否已经紧跟
=来返回不同textEdit对象。猜测如此实现是为了更好支持 deoplete 插件中提供的 fuzzy match 功能,因为 LSP 服务端可能对于补全前半部分做的是全匹配验证。
-
错误的处理返回的
TextEdit代码: https://github.com/autozimu/LanguageClient-neovim/blob/d7cac79c1dd2b7b644c07ff55c8208821ff1192e/src/types.rs#L419
text_edit.new_text应该就是服务端返回用于插入的文本,但是由于 LCN 无法支持text_edit中的range修改已有 buffer,所以只能去主观的截取第一个单词,然后移除末尾的非单词部分。该做法会极大影响部分 LSP 服务的使用,例如 https://github.com/vscode-langservers/vscode-css-languageserver-bin 补全 css 属性时new_text最后为空格,另外像 https://github.com/Microsoft/vscode-html-languageservice 会返回带有"的 snippet 补全项。
如何解决?
问题根源在与 neovim/vim 提供的 complete 功能无法支持 LSP 中的 TextEdit 以及 snippet 等定义,所以我提了一个需求 https://github.com/neovim/neovim/issues/8334