NoahOrblog

某・福島にある大学のコンピュータ理工学部の大学生のお話。

neovim/go-client入門

Noahです。

この記事は

adventar.org

↑の、13日目の記事です。

どうでもいいですが、GithubのContributionが4桁行きそうです

f:id:NoahOrberg:20171213222509p:plain

結構ひとりで感動してる


本題

Vim(ないしはNeoVim)のプラグインを書きたい人、いますよね???

Vimが好きなら誰しもが一度は作りたいと思ったことがあるはず。

標準で搭載のVimscriptを駆使してもいいですが、Goで書けると結構いいよ、というお話

前提としてGolangがチョットかけるのと、環境整備はしてあること、あとは .vimrcないしはinit.vimを設定したりしたことあるひとなら多分大丈夫です。

github.com

公式READMEの内容が(自分の英語力不足で)ちょっと最初理解できなかったのでそこら辺の敷居を低くする名目も兼ねて、READMEの内容をちょっとやります。


HelloWorldに相当するであろうものが以下です(一応、公式READMEにsampleとしてあるが、ファイル構造ガン無視だったりして最初戸惑ったのでいっそまとめました。)

github.com

ちなみに自分のNeovimのバージョンはv0.2.1ですが、多分どのバージョンでも出来ます。

あと、依存パッケージの管理にglideを使用してます

取り敢えず動かす

まずGOPATH以下に落として移動しましょう

$ go get NoahOrberg/helloworld.nvim
$ cd $GOPATH/src/github.com/NoahOrberg/helloworld.nvim

移動できたところで、中身を確認します。

$ ls
README.md   glide.lock  glide.yaml  main.go     plugin/

こんな感じになってるはず。

依存パッケージを落とすのとビルドをします

$ glide install
$ go install

そしたら、各自のinit.vimプラグインを追記します。

ここいらは各自のプラグイン管理ツール(NeoBundle, deinなど)によった書き方をしてください。

" deinの例
call dein#add("NoahOrberg/helloworld.nvim")

そしたらあとは、nvimを起動して、:echo Hello("world") と関数を呼び出してみてください

Exコマンドを打つところに Hello world と、こんな感じに出てくれば成功です。

f:id:NoahOrberg:20171213213621p:plain

これでVimscriptからアクセスできるHello関数を定義できました🎉

内部でやってることはそんなに難しくなくて、

  1. Golangで関数の定義をする。

  2. ちょっとvimscriptを書いてRegisterする

ことをやっているだけです

Golangで関数の定義をする

gist.github.com

9-11行目で関数をかいて、15行目で関数をOption付きでRPCで通信できるように登録してるだけです

ここの plugin.FunctionOptions{...} っていうのが肝で、これでArgsの数とか制限しないと、後述しますがスライス外参照でpanicしたりします。(関数内で引数の数見ればいいんですが、こっちのほうがよりスマートです)

例でやっていたのはただの関数なので、(string, error) を返す感じになっており、error が nil じゃなければNeoVim側でエラー返すようになっています。

ちょっとvimscriptをかいてRegisterする

gist.github.com

さすがになんでもGolangでは書けなかった様で、RPCをRegisterするあたりはvimscriptで書きます。

自動で読まれるplugin/ディレクトリに.vimファイルを置いてあります

10行目くらいまではだいたいお決まりなのですが、12行目からで関数、コマンドをRegisterします。

ここの 'name'Golangの方のコードで &plugin.FunctionOptions{Name: "Hello"} で指定した Name から引っ張ります。

ちなみに、このあたりはmanifestというのですが、Golangでビルドした後にバイナリを実行して、helloworld.nvim --manifest=hostname とやると、自動で作成できたりします(ちなみに、--manifest=hostname を付けて、さらに --location=file/path/.vim もつけると、Goコードから勝手に解析されて自動で.vimファイルが更新されます)

msg-packで書ける利点とか、いいところ

多分pythonや他の言語でもそうですが、REST APIと通信する際に非常にやりやすい点がまずひとつです

普段から触ってる言語なら、NeoVimとの通信部分以外はほぼほぼ気を取られることなく開発できます

あとは、他言語もそうですがNeoVimが起動中はデーモンのように後ろに張り付いて動いてるので、中で変数をもたせることも可能です。( GitHub - NoahOrberg/gilbert.nvim: gist client for NeoVim はそれを知らなかったのでvimscriptの変数へ無理やり出し入れしているため気持ち悪い)

構造体とか定義して、中で値をもたせるといいかもしれません。

ちなみに、GolangでExコマンドを定義することは出来ますが、キーにマッピングする際はvimscriptでやります

あと前回からの進展

開発しているなかで一番しんどいのは、以前も書きましたがmsg-packのchが謎にcloseする時です。

しかも、panicした旨(どこでしくじったか、等)が来なく、ただ chがcloseしたよ! と、お知らせが来るのみで NeoVim側に来ない

だいたいはスライスの範囲を超えて参照したりしてpanicしていたり、ライブラリがpanicしてるので、defer, recover 辺りを使っていい感じにいい感じするくらいしか対策がないですが、いい方法があったら教えて欲しい次第です。

最後に

リポジトリは前に作ってたのですが、記事は時間がなくかなり適当に書いているので、ちょっと端折ってるところとかあるかもですが許してください

ちなみにAizu Advent Calendarのコメントに書いてたお話は急遽

qiita.com

の 23日目でお話します。

暇な人、また読んでね。