当我在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 命令获取。

作为努力的方面,可以列举如下:

    1. 只是编辑Vim script并构建它是多余的,所以根据附带的package.json版本进行构建。

 

    还可以从Vim script这一侧引用package.json的版本,以便能够检测到需要更新二进制文件的情况。

这些设置被记录在 https://github.com/hrsh7th/vim-candle/blob/master/.github/workflows/release.yml。

实施后的感想

Golang真的很容易编写,性能也非常出色,非常好用。特别是gopls做得很好,自动补全、自动导入、诊断信息等功能都很稳定,我觉得作为LSP服务器它做得最好。

另外,我认为最初预计的实验进展是否顺利。

    1. 使用Golang进行过滤,运行速度快。

 

    1. 通过单一二进制文件具备自动下载功能,安装也很简单。

 

    可以使用yaegi进行外部扩展。

但是,仅仅能够完成想做的事情,并不能说这个插件的完善程度很高。首先功能很少。我们还需要发布、解决问题、完善文档,考虑到破坏性变更时的迁移路径……对于那些认真维护的前辈们,我敬佩之情溢于言表。

总结

在我实施某项功能时,我一边回忆是否有有趣的话题,一边写作,但当我试着将其写成文章时,效果不太好。实际上,我在完成这个任务时非常艰辛,花费了相当长的时间。

虽然作为一个初级项目,它只有基本功能,并且在设计方面有很多错误,但总的来说,它能正常运行,而且在实施过程中也很有乐趣,所以还不错!

广告
将在 10 秒后关闭
bannerAds