coc.nvim 不仅提供了完整了 LSP 功能支持,甚至提供了加载插件的能力。
做为一个可以加载插件的 vim 插件,很多人不是太理解,本文来做一些简单的介绍。
最主要的原因是仅有 LSP 支持无法实现像 VSCode 插件那样完整的功能。举一些例子:
VSCode 不同插件提供了许多配置项,大部分都是 server 使用的,但是也有一些是给 client 使用,如果不使用插件就需要在代码里对特定 server 进行不同适配,目前大部分 vim LSP 插件仅支持 server 使用的配置。
某些功能需要客户端做一些特定的扩展,例如 java 的 jdt.ls 需要客户端支持 jdt 开头的 uri,使用 coc-java 插件用户只需要安装插件,无需手工配置。
实现对于自定义请求 /事件的处理。例如 coc-rls 监听 rls 的自定义事件来更新状态栏信息。
问题修复。例如某些 server (css, json) 返回的补全项会包含非关键词,需要进行补全起始位置修复才能正确完成补全,通过 coc 插件的中间件方法可以修改响应或者请求,从而对于特定问题进行修复。
总体来讲,如果你的需求相对比较简单,还是使用 viml 开发最为有效,如果你想弄一些复杂点的功能同时又对 javascript/typescript 比较了解,可以考虑基于 coc 开发。相比传统的 viml 以及 python 插件,coc 插件有自己的优势。
优异的异步性能,coc 是独立于 vim 的 nodejs 进程,支持完整的异步通讯方法。可以用 viml
发送事件给 coc,也可以从 coc 发送事件给 vim 从而避免 request 所导致的进程阻塞。coc
甚至实现了通过 viml 异步调用 coc 方法获取结果(例如: CocRequest()),
以及在 coc 内异步调用 vim 的方法获取结果(例如 coc 的 vim 补全源统一使用回调接受结果) ,
这两种用法其它的 neovim 客户端暂时都不支持。相比于 python,javascipt 不仅有原生的 async await,
整个社区还有大量异步的轮子。
性能优化,coc 提供了 document change 事件,无需额外事件以及传输消耗即可实时获取所有
缓冲区内容,对于同时的连续 vim 事件(例如同时添加许多高亮区域),可以使用
nvim.pauseNotification() 以及 nvim.resumeNotification() 批量化发送事件到
vim,从而避免连续事件导致 vim 反复重绘引起卡顿。
基于 javascript 社区的模块,javascript 拥有最大的社区生态,你可以使用 /借鉴已有的开发成果。
使用 coc 提供的 API,coc 不仅提供了 languageclient 相关的接口帮助创建 LSP
插件,同时提供了许多其它方便的接口,例如通过 sources 模块可以轻松的添加补全
source,通过 list 模块可以创建高效且功能强大的列表。
统一化 vim 和 neovim 适配,coc 新版在传输层对于 vim 做了 neovim
接口的适配,不再需要 vim 用户安装 vim-node-rpc 以及支持
python。除了部分无法在 vim 上支持的 neovim 特性,可以直接使用 node-client
提供的接口。对于有区别的 job 和 terminal 接口,coc 提供了 task 和
terminal 模块用于控制 (neo)vim 的 job 和 terminal。
更加可靠的代码,基于 typescript 编写的代码相比与 viml 更加可靠而且可读, 同时 coc-tsserver 插件提供了 typscript 的完整支持,让编写 typescript 更加轻松。
方便的配置和管理,跟 VSCode 插件一样,coc 插件可以在 package.json
中配置启动事件、自定义设置项、json scheme 关联等额外信息,coc-json
可以加载插件的自定义设置项从而对用户的配置文件进行补全和验证。安装 coc
插件仅需要 CocInstall 命令,管理 coc 插件可以通过 :CocList extensions
来操作。
快速重新加载,无需重启 vim,使用命令 :CocRestart 可以重启 coc
服务,所有插件都会被重新加载,或者使用 g:coc_watch_extensions 配置同时安装
watchman 在插件代码变化后自动重新加载。
调试代码,最简单的方式是在插件中使用 console.log,
输出内容会重定向到 coc.nvim 的日志,复杂一点的问题可以使用 Chrome
来调试,只需设置 let g:coc_node_args = ['--nolazy', '--inspect-brk=6045'],
然后 Chrome 打开 chrome://inspect 页面找到对应 target。
如果你不清楚 coc 插件是否可以取代某个插件,我个人建议看一下插件文档, 如果没有你特别想用的功能或者你要用的功能它没有那就没必要换(也可以提 feature request )。
例如我们想请求 lbdbq 里面的 email 列表用来做 email 补全。
首先找到配置的根目录,使用命令 :echo coc#util#get_config_home(), 通常结果是 ~/.vim,
在该目录下执行 mkdir coc-extensions && touch address.js 创建 coc-extensions
文件夹以及 address.js 文件。
在 address.js 文件内加入:
const {sources} = require('coc.nvim')
const {spawn} = require('child_process')
const readline = require('readline')
exports.activate = async context => {
context.subscriptions.push(
sources.createSource({
// 唯一 id
name: 'notmuch',
// 用于 menu
shortcut: 'address',
// 指定 filetype
filetypes: ['mail'],
// 仅在触发条件满足时触发
triggerOnly: true,
priority: 99,
triggerPatterns: [
/^(Bcc|Cc|From|Reply-To|To):\s*/,
/^(Bcc|Cc|From|Reply-To|To):.*,\s*/
],
doComplete: async function(opt) {
let matches = await getAddresses(opt.input)
return {
items: matches.map(m => {
return {
word: `${m[1]} <${m[0]}>`,
abbr: `${m[0]} ${m[1]}`,
filterText: `${m[0]} ${m[1]}`,
menu: this.menu
}
})
}
}
})
)
}
async function getAddresses(input) {
let result = []
return new Promise((resolve, reject) => {
const p = spawn('lbdbq', [input])
const rl = readline.createInterface(p.stdout)
p.on('error', reject)
rl.on('line', line => {
if (line.startsWith('lbdbq:')) return
let [email, name] = line.split('\t')
result.push([email, name])
})
rl.on('close', () => {
resolve(result)
})
})
}
coc-extensions 目录下的所有 js 文件都会被 coc 当作插件加载,暂时无法支持
package.json 配置。
下一篇介绍 CocList, 一个和 fzf 一样快同时像
denite.nvim 一样强大的列表功能。
支持 coc.nvim 请扫描项目主页最下方二维码,同时获取更多支持者福利。
转发 CSDN 按侵权追究法律责任,其它情况随意。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.