LoginSignup
11
10

More than 5 years have passed since last update.

Vim/Neovim の terminal mode の中で Vim/Neovim を実行するときに便利そうなやつを作った

Posted at

つくった


画像は https://ghlinkcard.com/ から生成
参考: みんなにOSSを見てもらいたい人の為に、GitHubリポジトリのOGP的画像を自動生成してくれるサービスを作った

どういうやつ?

Vim/Neovim の terminal mode で Vim/Neovim を実行すると Vim/Neovim が入れ子に起動してしまう
asciicast

これを回避するには Neovim の場合は mhinz/neovim-remote を使うとよい
asciicast

Vim8 の場合でも clientserver 機能や terminal-api を利用すればよい
参考(clientserver): Vim の :terminal の中から外の Vim を操る方法
参考(clientserver): :h clientserver
参考(terminal-api): :terminal に関する小さい Tips
参考(terminal-api): :h terminal-api

しかしいずれにせよ、vim/nvim の中と外で実行するコマンドを切り替えねばならんというのは面倒である
なので、自身が vim/nvim の中なのか外なのかをよしなに判断して上記の回避策を実行してくれるツールを作成した
先行研究もあったが、Vim8 のみ対応のようだったので Neovim にも対応する形で実装してみた
参考: Vim in Vim しない :terminal

どうやってつかう?

こんな感じ
asciicast

あとは alias vim=vimalter みたいな感じで shell の alias 登録すれば、どこでもとりあえず vim って実行すればいい感じに vim/nvim が開く

なにをしている?

vimalter コマンドを実行しているのは vim なのか nvim なのか shell なのか

vim/nvim は terminal mode のとき、いくつか環境変数を追加してくれているので、それを利用した
環境変数 VIMRUNTIME に runtimepath を入れてくれているようなので(ex. VIMRUNTIME=/usr/share/nvim/runtime)一つ上のディレクトリ(ex. /usr/share/nvim) の部分が nvim なのか vim なのかで判定した

もしかすると自前で vim/nvim を build してたりするとここに vim or nvim が入っておらず、上手く動かないかもしれない

neovim の場合

単純に引数を nvr コマンドに食わせて実行すればよい

vim の場合

vim は少し複雑で :term する前に call remote_startserver('hogehoge') を叩いているかどうかで挙動を変える必要がある
remote_startserver()+clientserver オプションを付けて build された vim でしか実行できない関数で、引数に指定された名前(例では hogehoge)で vim サーバを起動するコマンドである

これが実行されていれば terminal mode では 環境変数 VIM_SERVERNAMEhogehoge と入っているので vim --servername $VIM_SERVERNAME --remote file_name というコマンドを実行すれば親の vim で file_name を開いてくれる

call remote_startserver('hogehoge') されていない場合は環境変数 VIM_SERVERNAME は空なので他の方法を考える必要がある
今回は terminal-api を利用した

terminal-api の詳細は先に挙げた参考資料に任せるとして、とにかく echo -e \x1b]51;[\"drop\",\"file_name\"]\x07 と terminal mode で実行すれば親の vim で file_name に指定したファイルを開いてくれる

vim の実行ファイルを探索する

/usr/bin/vim が vim だといつから錯覚していた?

はい、debian 系(というか update-alternatives を使用している場合などは大抵) /usr/bin/vim とはただのシンボリックリンクである
実態はどこにいるかは辿ってみないと分からないので雑に vim に引数を食わせるわけにはいかない
ちゃんと現在 terminal mode で実行中の vim の実 path を辿ってやる必要がある
方法は愚直に PPID を辿って vim または vim.basic(debian で apt で入れた vim の実行ファイル名はこれになる) という文字列が実行ファイル名になっているものを探す
apt でインストールされた vim の実行ファイル名は他にも vim.tiny とかもあるが、vim.tiny は terminal mode が使用できないので除外した
他にも実行ファイル名のパターンがあれば Issue か PR をください...!
この実装をする際 shirou/gopsutil が非常に便利だった

-tab option

Vim の --remote オプションは親でファイルを開く際、ファイルをウィンドウ分割して開く、という挙動となっており、個人的には --remote-tab の挙動のほうが好みだったので -tab オプションで制御できるようにした
弊害として、vim 側のオプションを渡す際は -- を付ける必要があるが、まあ大抵こういうコマンドはそうなってるのでいいかな、と(適当)

vim --remote-tab だと、既存に空ファイルのウィンドウがあればそのウィンドウでファイルを開き、それ以外の場合は新規に tab を作成してそこでファイルを開く、という挙動をする

このとき困るのは terminal-api での実装で、現状 drop コマンド(つまり挙動としては --remote と同じ)しか雑にファイルを開くときには使えないので --remote-tab を真似ることはできない
call コマンドというユーザ定義関数を呼ぶコマンドもあるにはあるが、Tapi_ で始まるユーザ定義関数しか呼べない、という制限があるため現状は未実装ということにしている

今後の予定として Tapi_ 関数群を提供する Vim plugin を書いて、この挙動を実装する方針である

11
10
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10