当我在Golang中创建一个同时在Vim和neovim中运行的选择器插件时的经历
首先
在这篇文章中,我将讲述关于在Vim/neovim上编写的一个Go语言插件的故事。我将大致介绍在编写插件时所考虑的各种内容。
起源
最近我一直在自己制作和使用LSP客户端、代码片段插件和自动补全插件等。虽然这很有趣,但我常常自问自答,不知道自己到底在做什么?时间过得飞快,这是肯定的。
顺便说一下,那时我想尝试的是类似于 fzf 或 denite 的选择器插件。
在制作选择器插件时,我觉得性能可能会很重要。
-
- Vim script で 1 万件のファイルをフィルタしたりって現実的…?
-
- Golang 触ってみたい
- Golang でシングルバイナリなプラグイン書けばインストールも手間いらずでいいのでは?!
出于这种考虑,我决定尝试用Golang编写插件。
产出
※ 因为我认为现有的插件功能更强大且稳定,所以不推荐使用。
※ 暂时可以运行。
在实施时考虑的事情
与 Golang 进程协同工作的方法。
如果是Neovim,提供了一种名为远程插件的本地方式,可以通过msgpack-rpc与其他进程交互。但是,如果要支持Vim,则需要自己进行一些相关的开发工作。
由于我原本就是LSP客户端的爱好者,所以我已经准备好处理JSON-RPC的代码了。(vim-vital-vs)
所以,我们决定使用 Golang 进程通过 stdin/stdout 来进行 JSON-RPC 通信。这种方法是大多数在 Vim 中运行的 LSP 客户端所采用的方法。
我在Golang中使用了sourcegraph/jsonrpc2来处理JSON-RPC。(参考了mattn/efm-langserver!)
实际上,这个库不只是纯粹的 JSON-RPC,它还包含了头部部分,比如 Content-Length,这是根据 LSP 定义的协议。但是,上述的库也能兼容这些内容。
如何确保可扩展性?
各种选择器插件(如fzf和denite)都采用了用户可扩展的设计。
fzf
fzf 起動時にコマンドを渡すことで拡張できます。
grep 結果がほしければ rg -i … を渡せるし、ファイル一覧が欲しければ fd … を渡せます。
denite
denite は python で記述したスクリプトを動的に読み込んでくれます。
例えば、下記のような拡張が denite の外部ソースとして公開されています。
https://github.com/delphinus/vim-denite-memo
https://github.com/neoclide/denite-git
https://github.com/hsawaji/denite-ctags
既然是自己做的,就要考虑到可扩展性。这是我自己心里想的。(虽然我知道只会在自己使用的范围内进行扩展。。。)
我曾经考虑了很多,但最终决定引入某种脚本处理系统。
https://github.com/d5/tengo
Golang で書かれたインタプリタ(VM)
高速に動作するらしい。独自の文法を持っている。(Golang に寄せてはいる模様)
最初はこれにしようかなーと考えていました。
https://github.com/yuin/gopher-lua
Golang で書かれた Lua のインタプリタ
Lua はこういう埋め込みの言語には向いてるとよく聞くし、候補に入れていました。
https://github.com/traefik/yaegi
Golang で書かれた Golang のインタプリタ
Pure Go で書かれたスクリプトなら動的に読み込み可能!
さらに、登録すればスクリプト側からバイナリ側の関数も実行可能!
元々 Golang 勉強したいって話ではじめたし、これでいってみよう!となりました。
现在我正在使用yaegi编写源码,它运行得相当正常。
通过指定 Golang 的脚本文件,可以在 Vim 脚本的一侧发送请求来执行扩展源代码。
希望能够简便地安装。
我们今天的任务是将 Golang 代码转换为预编译的二进制文件进行发布。虽然对于 Go 编程爱好者来说这是很常见的配置,但我还是想通过以下流程实现自动安装。
- 使用 GitHub Actions 来触发 goreleaser,并生成二进制文件,然后将其注册到 GitHub Releases 中,再通过 Vim 插件使用 curl 命令获取。
作为努力的方面,可以列举如下:
-
- 只是编辑Vim script并构建它是多余的,所以根据附带的package.json版本进行构建。
- 还可以从Vim script这一侧引用package.json的版本,以便能够检测到需要更新二进制文件的情况。
这些设置被记录在 https://github.com/hrsh7th/vim-candle/blob/master/.github/workflows/release.yml。
实施后的感想
Golang真的很容易编写,性能也非常出色,非常好用。特别是gopls做得很好,自动补全、自动导入、诊断信息等功能都很稳定,我觉得作为LSP服务器它做得最好。
另外,我认为最初预计的实验进展是否顺利。
-
- 使用Golang进行过滤,运行速度快。
-
- 通过单一二进制文件具备自动下载功能,安装也很简单。
- 可以使用yaegi进行外部扩展。
但是,仅仅能够完成想做的事情,并不能说这个插件的完善程度很高。首先功能很少。我们还需要发布、解决问题、完善文档,考虑到破坏性变更时的迁移路径……对于那些认真维护的前辈们,我敬佩之情溢于言表。
总结
在我实施某项功能时,我一边回忆是否有有趣的话题,一边写作,但当我试着将其写成文章时,效果不太好。实际上,我在完成这个任务时非常艰辛,花费了相当长的时间。
虽然作为一个初级项目,它只有基本功能,并且在设计方面有很多错误,但总的来说,它能正常运行,而且在实施过程中也很有乐趣,所以还不错!