CtrlPとGitのls-filesサブコマンド

CtrlPのヘルプのカスタム・コマンドの項に記載があるように、Gitのls-filesサブコマンドを使ったカスタム・コマンドを定義すると、.gitignoreを考慮してファイルをリストアップすることができる。ただ常にこちらである必要はないので、Git管理下だけでそうなるようにしたい。その場合、ラッパー関数を作成して、その中でb:ctrlp_user_commandを設定してやると良さそうだ。

Git配下のファイルかどうかでCtrlPを切り替えるで作成した関数を元にして、v:shell_error0の時も特別視してやるようにすることで実装した。カスタム・コマンドはヘルプのまま、Gitリポジトリーのルートへ移動してls-filesサブコマンドを呼んでいる。

nnoremap <Leader>f :call <SID>CallCtrlPBasedOnGitStatus()<Return>

function! s:CallCtrlPBasedOnGitStatus()
  if exists('b:ctrlp_user_command')
    unlet b:ctrlp_user_command
  endif

  let s:git_status = system('git rev-parse --is-inside-git-dir')

  if v:shell_error == 0
    let b:ctrlp_user_command = ['.git', 'cd %s && git ls-files']
    execute 'CtrlP'
  elseif v:shell_error == 128
    execute 'CtrlPCurFile'
  else
    execute 'CtrlP'
  endif
endfunction

まずバッファー・ローカルの変数にカスタム・コマンドの設定があったら削除しておく。そうしないと変数の型の問題で、定義を上書き出来なかったりすることがあるからだ。

その後、Gitのrev-parseサブコマンドを--is-inside-git-dirオプションを指定して呼ぶことで、カレント・ディレクトリーがGit管理下かどうかをチェックする。一応標準出力は拾っておくが、判断は終了コードのみで行う。

終了コードが0の場合はls-filesサブコマンドを呼ぶようにバッファー・ローカルの変数を設定してやり、普通にCtrlPを呼ぶ。128は管理下でなかった場合なので、その時は編集中のファイルのディレクトリーを使ってCtrlPを呼ぶようにCtrlPCurFileを呼ぶ。変な終了コードが返ってきた場合のフォールバックとして、カスタム・コマンドを設定せずにCtrlPを呼ぶようにもしておいた。

うまく動いている気がする。autocommandを使ってバッファーを読み込んだ時にb:ctrlp_user_commandを設定しても良さそうだったが、バッファー開く度にGitが呼ばれるのはちょっとコストが高い。なのでCtrlPを呼ぼうとした時にフックするように作った。CtrlPをつかう頻度によってはautocommandの方が効率的かもしれない。


ただし、WindowsのVimでset shell=shかつset shellslashしている場合、CtrlPがカスタム・コマンドを呼ぶ時にカレント・ディレクトリーのパス情報をバックスラッシュに変換してしまうためうまく動かない。CtrlPの修正が必要になるようだ。僕はとりあえずautoload/ctrlp.vimの405行目をコメントアウトして凌いでみている。


コマンドの短縮形ばかりで書かれているVimスクリプト読みづらいな。