nazo6 noteblog

Typescriptでneovimの設定を書く!

作成:2021/03/28

更新:2021/10/23

この記事は、Zennにも投稿しています。
vim を使い始めて 2 週間ほどたったある日、せっかく neovim を使っているんだし設定が少ない今のうちに init.vim を init.lua に書き換えようと思いこちらの文章を読んでいたところ、最後にこんなものがあるのに気づきました。
その他の興味深いプロジェクト:
TypeScriptToLua/TypeScriptToLua
これはもしかして Typescript で vim の設定が書けるのではないかということでやってみたという話です。

導入

neovim の config 内に新しく ts フォルダを作ってそこに Typescript 関連の諸々を書いていきます。ちなみに自分の環境は Win10 ですがいろいろ違うところがあると思うので適宜読み替えてください。あと個人的にpnpmを推しているので使っていますがそれも適宜読み替えてください。
$ mkdir ts
$ cd ts
$ pnpm init -y
$ pnpm i -D typescript-to-lua lua-types
npm scripts を追加します。
"scripts": {
"build": "tstl",
"dev": "tstl --watch"
}

実行してみる

まずは tsconfig.json を作ります。TypescriptToLua では多くの tsconfig のオプションがサポートされていてさらに固有のオプションも存在します。
ts/tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"lib": [
"esnext", "DOM"
],
"moduleResolution": "node",
"strict": true,
"rootDir": "./src",
"outDir": "../lua",
"types": [
"lua-types/5.3"
]
},
"tstl": {
"luaTarget": "JIT",
"noImplicitSelf": true
}
}
ソースコードは src 以下に入れるつもりなので rootDir を設定します。また neovim は lua フォルダにある lua ファイルを自動で読み込むのでそこに lua が生成されるようにします。
次に、とりあえず ts 用のエントリポイントとして src/tsinit.ts を用意します。
ts/src/tsinit.ts
console.log("Hello world from ts!");
pnpm devを実行すれば tsc と同じように保存したら自動でビルドしてくれます。
最後にビルドされた tsinit.lua を呼び出すだけの init.lua を作ります。
init.lua
require("tsinit")
とてもシンプルでいいですね。ではここで neovim を起動します。
Hello world from ts!
と表示されたら成功です。
ちなみにここでは ts が使えるということを示すために console.log を使っていますが tstl がいい感じに変換してくれているので動きますがこういった対応がされているものはあまりないので lua や vim のメソッドを使った方がよいでしょう。

vim の設定を書く

さて、これで TS で vim の設定が書けることはわかりましたが次に vim の設定をしてみたいと思います。といっても何も考えなくても lua と同じ通りに書けば呼び出せます。
というわけでとりあえずいくつか設定してみました
ts/src/tsinit.ts
console.log("Hello world from ts!");

vim.api.nvim_set_option("number", true);

vim.api.nvim_set_option("tabstop", 2);
vim.api.nvim_set_option("shiftwidth", 2);
vim.api.nvim_set_option("expandtab", true);

vim.api.nvim_set_option("clipboard", "unnamedplus");
では実行してみます
...
エラーが出ましたね。vim なんてものは存在しないんですから当然ですね。
とりあえずここでは型定義を作成してしのぎます。
ts/@types/vim.d.ts
declare namespace vim {
const api: {
[key: string]: (...args: any[]) => any;
};
}
これで問題なくビルド・実行できるはずです

型をつけたい

ここまでで一応 Typescript で neovim のコンフィグを書くことはできると分かってもらえたかと思います。しかしまだ足りないものがあります。そう、型です。Typescript なら型安全に開発したいものです。といっても型定義なんてあるはずがありません。というわけで自分で作りました。
https://github.com/nazo6/nvim-types
neovim のドキュメントから半自動で型定義を生成しています。
なのですが実は型定義はできておらず、関数名と JSDoc コメントによる詳細の表示にとどまっています・・・まあしかしそれでもこれでだいぶ楽になるのではないでしょうか。
ともかく nvim-types をインストールします。
$ pnpm i -D nvim-types
このままでは Typescript が型定義を認識してくれないので tsconfig で指定してあげます。
"types": ["lua-types/5.3", "nvim-types/0.5.0-nightly"]
これで vim api の型が読み込まれます。こんな感じで補完も効くようになります。(any ですが・・・)
https://storage.googleapis.com/zenn-user-upload/06ux5lrkppt0av3359iu3r7322y1
ここまでやってほかにもいろいろ設定してる自分の設定フォルダがこちらになります。
https://github.com/nazo6/nvim/tree/81900fa3afccf21bdd6c68934aeb964a00137adf
dein の設定とかもあるので少しは役に立つのではないかと思います。

課題

  • プラグインとかは型が効かない
  • まあ型は書けばいいのですが TypescriptToLua の仕様上どうしても.ではなく:で呼び出されるところが出てきてしまいうまく動かなくなります。@noSelfで回避できるのですが型によってはうまくいかなくて苦労しています。
  • ビルドがめんどくさい。ビルドし忘れてあれ?動かないということがよくある。tstl を neovim のターミナルで動かしてると設定反映の再起動で消える
  • ここまでして Typescript で書く意味って・・・?(おい

最後に

というわけで neovim の設定を Typescript で書く方法について書いてきました。同じ仕組みで neovim 向けプラグイン開発もできるはずです(やったことないけど)。また他にも lua に変換できる言語はすべて neovim の設定/プラグインが書けるはずです。
ちなみに nvim-types はドキュメントをからかなり無理をして情報をとってきているのでもっと何かいい方法を考えたいなーと思っています(今考えているのは neovim のソースファイルから持ってくること。だがそれも vim.api 以下のみ)。流石に手動ではやりたくないです・・・
vimscript はとっつきづらいところがあると思うので vim いなと思います。