Unite.vimでgitリポジトリのトップレベルディレクトリを開く

土曜日はずっとこれやってた気がします。
既存のプラグインとか使うと出来る気もしていますが、Vim Scriptの勉強がてら自分でやる方法を模索してみました。

gitのトップレベルに移動する方法は以前Qiitaに投稿した通り。
gitのトップレベルディレクトリに移動する

このコマンドを使ってパスを取得して、Uniteのpathに指定してあげればいいと。
色々探してて、外部コマンドの結果をバッファに展開する方法はたくさんでてきたけど
変数に格納する方法がわからず。
他の人の書いたプラグインを読んでみたところ、system関数を使えば結果を文字列として得ることができるらしいのがわかったのでこれを使いました。

まずはじめに

let l:isgitrepo = system("git rev-parse --is-inside-work-tree)

でgitリポジトリの中にいるかどうかを確認。
中にいればtrueが返ってくるのですが、改行文字が末尾に入っているので

let l:isgitrepo = matchstr(system("git rev-parse --is-inside-work-tree"), "true")

でtrueが含まれているかどうかをチェック。
matchstrで正規表現パターンを指定して、マッチする部分を抜き出してくれるらしいです。
改行文字を取り除く方法もあるのですが、それは後述。

gitリポジトリの中にいればトップレベルディレクトリのパスを取得できるはずなので
さっきと同様にsystem関数を使って外部コマンドを実行。

if l:isgitrepo == "true"
    let l:gitroot = system("git rev-parse --show-toplevel")
    return substitute(l:gitroot, '\(\r\|\n\)+', '', 'g')
endif

substituteはパターンにマッチする部分を置換してくれる関数みたいです。

:s/aaa/bbb/g

のことみたいです。

gitリポジトリの中にいない場合はカレントディレクトリを指定できればいいので"."を返すように。
それを関数化しました。

function GetGitRoot() abort
  try
    let l:isgitrepo = matchstr(system("git rev-parse --is-inside-work-tree"), "true")
    if l:isgitrepo == "true"
      let l:gitroot = system("git rev-parse --show-toplevel")
      return substitute(l:gitroot, '\(\r\|\n\)\+', '', 'g')
    else
      return "."
    endif
  catch
  endtry
endfunction

最後にUnite fileするときにこの関数を使ってpath指定をすればいいので

noremap <silent> <Space>uf :exe ":Unite -winheight=15 -path="
  \ . GetGitRoot() . " file"<CR>

としてあげました。
これでなんとか目標は達成です。
これやるのになんだかんだで半日ぐらいかけた気がする。
Vim Script怖い。