(´ワ`)

主に技術系備忘録、たまに日記。

coc.nvimのjumpCommandをカスタマイズする

まえがき

僕は今こそVim(or neovim) + Lspを使用してコーディングをしているが、数年前まではIntelliJ IDEA Ultimateを使っていた。 よく利用していた機能の一つとしてコードの変数や関数への参照先へジャンプする機能だ。(ソースコードナビゲーションの一種) これは新しいタブで参照先を開くため、参照先と参照元を見比べてコーディングができた。また、参照先が同じファイルだったりすると同じタブ内で開かれるので無駄なタブを開かれずに済む。これは些細でありながら素晴らしいUXだった。(本当にこういった用途かはわかりませんが少なくとも僕は助かった) 一方現在利用しているcoc.nvimでもこれと同様の機能が可能。しかし、IntelliJほどUXに優れた形で制御することはできない。 今回はIntelliJに近い形にcoc.nvimの設定を見直した内容を備忘録として残す。

coc.nvimのjumpCommandの設定

デフォルトでは現在のウィンドウにそのまま定義先へジャンプする。一応これを別タブ、あるいは別ウィンドウへ移動する設定はある。以下はコードジャンプの度に vsplit して開く例。(詳しくは :help coc-config-preferences から設定を検索)

// coc-settings.json
{
    "coc.preferences.jumpCommand": "vsplit" // vsplitしてジャンプする
}

しかしこれだと参照先が同じファイルでも別ウィンドウに同じファイルを開いてしまい、非常に無駄。なぜならばVimmerはめちゃめちゃウィンドウ開くため定義先が同じファイルなのに、そのためにウィンドウを増やしたくないから・・・

本題とは違うがこれを解決するヒントはすでにissueに上がっていた。

github.com

開発者は拡張性を持たせるためか、ちゃんとカスタマイズできる術を用意してくれていた。ありがとう。

独自の関数で制御する

coc.nvimのコードジャンプの際に返ってくる値は参照先のファイル名と該当する変数・関数のカーソル位置だ。そのため、これを用いてコントロールさせることができる。具体的には以下のようになる

  • 参照先ファイル名が同じ場合は edit コマンドで開く
  • 参照先ファイル名が違う場合は split 、あるいは vsplit で開く

これらでこのように制御する

function! s:coc_code_jump(cursor, name) abort
    if a:name == @%
        execute('edit ' . a:name)
    else
        " ひとまずvsplit, tabに開かせたかったらtabeにする
        execute('vsplit ' . a:name)
    endif
    execute(a:cursor)
endfunction

command -nargs=* CocCodeJump call s:coc_code_jump(<f-args>)
// coc-settings.json
{
    "coc.preferences.jumpCommand: "CocCodeJump"
}

これをruntimepathに含めておくことで上記の仕様に沿った制御ができる。

個人的には狭い方向にvsplitされるのは困るので広い方へ自動にジャンプしてくれるように設定した。その設定は以下( s:auto_split_for_coc() )

github.com

これで幅か高さが大きい方に対してウィンドウが自動的に開かれるのでウィンドウを移動させる無駄がない。

これによりIntelliJと同様の機能を果たせた。しかもそれ以上に開く方法をウィンドウかタブか切り替えることもできる。すばら

追記(2020/07/07)

昨日の更新で引数に call cursor() を渡さなくなった。(redrawをせずにバッファから読み込むように最適化した改修による) https://github.com/neoclide/coc.nvim/commit/5d0557f13f6c03cc3ff3a54bdc08755ea9318d4f

それに伴って上記の設定は以下のように修正できる(cursorを消しただけ)。なお coc-settings.json の変更は必要ない

function! s:coc_code_jump(name) abort
    if a:name == @%
        execute('edit ' . a:name)
    else
        " ひとまずvsplit, tabに開かせたかったらtabeにする
        execute('vsplit ' . a:name)
    endif
endfunction

command -nargs=* CocCodeJump call s:coc_code_jump(<f-args>)