🪆

Neovim本体の機能だけで入れ子起動を回避する

に公開

nvimの内蔵ターミナルでnvimを呼ぶとマトリョーシカのように入れ子でnvimが起動してしまいます。これを回避するためには様々な方法があって、よく使われるのがneovim-remote(nvr)です。
https://github.com/mhinz/neovim-remote

でも実は、二重起動を回避するだけならnvim本体の機能だけで実現できます。

Neovimの場合、組込みターミナルで起動すると$NVIMという環境変数が定義されます[1]$NVIMには現在のnvimのプロセスが使っているUnixソケットが格納されており、これを活用することでnvrを用いずにコマンドやファイルを開く命令を送信できます。昔は$NVIM_LISTEN_ADDRESSという名前だったのですがいつの間にか$NVIMに変わっていました。

二重起動を回避するには、この$NVIMが定義されているかを判定して、定義されていないなら新規起動、定義されているなら$NVIMのインスタンスで開くという動作をすればいいです。よって、以下のシェルスクリプト関数によって実現できます。ちなみにcommandというのはシェルスクリプトの設定などを無視して直接コマンドを探して呼び出すシェルに内蔵されている機能です。これがないと、無限ループに陥ってしまいます。

function nvim {
  # $NVIM が設定されていない (空である) かどうかをチェック
  if [ -z "$NVIM" ]; then
    # 設定されていない場合: -p オプションで通常起動
    # "$@" は関数に渡された全ての引数を正しく渡します
    command nvim -p "$@"
  else
    # 設定されている場合: --server オプションで指定されたサーバーに接続

    if [ "$#" -eq 0 ]; then
      # nvim内で起動するとき、remote-tabに何のファイルも渡さないとエラーが発生するので
      # 引数がないときはnvim内のコマンドを利用するように促す
      echo "Use :tabnew or :enew instead."
      return 0
    fi
    # winfixbufがnvim内のターミナルで設定されているとエラーが発生して開けないので--remote-tabで代用
    command nvim --server "$NVIM" --remote-tab "$@"
  fi
}

なお、--remote-tabを使う場合、引数にファイルが1つもない場合エラーを発生させてしまうためnvim内で開くように促して終了するようにしています。--remote-sendを駆使すれば新規タブも実現できると思います。

そもそも上記の例で--remoteではなく--remote-tabを使っているのは、自分の設定だとtoggleterm起動時にwinfixbufを設定しており--remoteのみでは開くことができないからです。逆に言えば、ターミナルでwinfixbufを設定していないのであれば--remoteで特に問題ないと思います。

winfixbufに対処するにはnvim側の設定を変更する必要があるため、別の機会に考えてみようと思います。

脚注
  1. 厳密にはnvim起動時に環境変数として$NVIMが設定され、子プロセスであるターミナルのシェルに引き継がれているようです。 ↩︎

Discussion