RustでNeovimのプラグインを作ってみる 〜サンプル編〜

2021-12-19

なにこれ

年末年始でRustの経験値を増やしたいので何か作ってみようと思います。
今の職場に変わってからRustを使い始めたのですが、もっと場数踏まないとな〜と漠然に思うようになりました。
実務で行っているWebアプリ関連とそこまで関係ない領域で何かできないかと考えてNeovimのプラグインでも作るか。と思い立ちました。
もちろんVim/Neovimのプラグイン作成経験は全くありません😇

なにから手をつけるか?

RustでNeovimのプラグインを作成できることは技術記事でみかけたことはあるのですが、そもそもNeovimのプラグイン作成ってどうやるのかも分かっていません。

全体の流れを掴むためにググって引っかかった以下の記事を手を動かして試していきます。
Writing Neovim plugins in Rust

なお、Vim対応まではしません。
というかいきなりそこまでハードルあげると途中で辞めてしまうと思うので。

作ってみた

ということで作ってみました。
otegal/neovim-plugin-sample-by-rust

数字の合算処理だけなので面白いことはなにもしていません。
:Add 1 4とコマンドモードで入力すると、Sum: 5と返ってくるだけです。

ざっくりとどんなことをしているかまとめていきます。

なぜNeovimのプラグインをRustで書けるのか?

なぜなのか?

Neovimリポジトリのwikiに書いてありました。
Plugin UI architecture

remote plugin (rplugin) という機構を用意していて、MessagePack-RPCを用いてプログラムとNeovimをやりとりさせることができるようにしているためのようです。
Neovimのプロセスを外部から制御できるように設計されているため、RPCを呼べればプログラミング言語は問わないということですね。
多少はVim scriptやLuaでグルーコードを書く必要はありますが、主要な機能を好きな言語で書けるのは面白いと思います。

どんな内容を書くのか

今回はRustとVim scriptで書いてみました。
それぞれどのような処理を行うのかを整理します。

Rust

Neovimのイベントを受け付けて、イベントに応じて実行する処理をハンドリングさせます。
MessagePack-RPCのクライアントとしてneovim-libを利用しました。
これをイベントハンドラとして扱っていきました。

処理の流れはだいたいこんな感じです。

  • Neovimからのイベントを受け付ける
  • イベントとパラメタからどの処理を行うかハンドリングする
  • 具体的な処理を行う
  • 処理結果をNeovimにこのコマンドを実行しろと通知する
Vim script

こちらはプラグインをを扱うための簡単な設定を書く程度でした。
主に行っていることは以下の通りです。

  • Rustで作成されたバイナリのパス指定
  • ジョブ起動
  • プラグインに渡すイベントとパラメタの定義
  • コマンドをNeovimから呼び出せるようにする設定

この辺は正直そんなにわかっていないのですが、このくらいであれば都度調べるのでもなんとか行けそうな気がする。

動作確認でハマった

作るもの自体はサクッと進んだのですが、自作プラグインをNeovimで読み込むところでハマりました。
すごくハマったので、むしろサンプル作成の段階で当たってよかったです。(と思いたい)

苦戦その1

プラグインマネージャにdein.vimを利用しているのですが、ローカルのプラグインはどうやって読み込むのかわからず苦戦しました。
doc読んだりdein.vimのコードを読んだりしていたのですが、結局tomlにローカルのパスを指定すればOKでした。
情報見つけづらかったな。

苦戦その2

ローカルのパスを指定するところまではわかったのですが、パスを指定しても一向に読み込まれない。
Not installed plugins: ['neovim-plugin-sample-by-rust']とエラーを吐いて、プラグインを読み込めず。

:mesでエラーメッセージを読み返したり、
:echo dein#get_log()でdein側のログ見たり、
:scriptnameでプラグインを読み込めているか確認したりしていました。 init.vimを少しづつ変更を加えて試したりもしました。

結局のところショボい間違いでした。。。

*.vimファイルを指定するものだと思っていたのですが、そうではなくプロジェクトルートの指定をするものでした。
プロジェクトルート配下のpluginやautoloadなどを読み込んでくれるようでした。

間違ってたもの
[[plugins]]
repo = '~/foo/neovim-plugin-sample-by-rust/plugin/neovim-plugin-sample.vim'
動いたもの
[[plugins]]
repo = '~/foo/neovim-plugin-sample-by-rust'

やってみた感想

激ハマりがあって疲れましたが、RustでNeovimのプラグインが作成できるの面白いですね。
MessagePack-RPCを利用すれば他の言語でも作れますね。
次は実用的なものを作成してみようと思います。

これVim対応できないんじゃない?と思ったのですが、自分がVimは使っていないのでまあいいかと割り切ります。
となるとグルーコードもVim scriptではなくLuaで書いてみるかと思ってきました。