🏡

Vimの起動時にpwdをgitルートへ移動する

2022/08/06に公開

ファイルツリーやfuzzy-finder系のプラグインを使っていて、常にプロジェクトルートからファイルを探したかったので作りました。

以下を設定ファイルに記載してください。

.vimrc or init.vim
" {{{ ensure_git_root
function! s:ensure_git_root() abort
  let cmd = 'git rev-parse --show-superproject-working-tree --show-toplevel 2>/dev/null | head -1'
  let root = system(cmd)->trim()->expand()
  if isdirectory(root) && root != getcwd()
    execute 'cd' root
  endif
endfunction
autocmd VimEnter * ++once call s:ensure_git_root()
" }}}

以下のようなディレクトリ構成のプロジェクトで、dir内でVimを開いたときに、pwdがmy_projectに移動するようになります。

my_project/
├─ .git/
├─ dir1/
│ ├─ file-inner1
│ └─ file-inner2
├─ file-outer1
└─ file-outer2

参考
https://qiita.com/dalance/items/b04c288d8dde06ce2b59


追記 非同期実行バージョン

function! s:ensure_git_root() abort
  function! s:cd_to_root() abort
    let root = get(s:, 'git_root', '')
    if isdirectory(root) && root != getcwd()
      execute 'cd' root
    endif
  endfunction

  if exists('s:git_root')
    call s:cd_to_root()
    return
  endif

  function! s:on_stdout(_, msg, ...) abort
    if exists('s:git_root')
      " set once
      return
    endif

    " a:msg is list in neovim, string in vim
    let s:git_root = type(a:msg) == v:t_list ? a:msg[0] : a:msg
    call s:cd_to_root()
  endfunction

  let cmd = 'git rev-parse --show-superproject-working-tree --show-toplevel'
  if exists('*jobstart')
    " neovim-async
    call jobstart(split(cmd), { 'on_stdout': function('s:on_stdout') })
  elseif exists('*job_start')
    " vim-async
    call job_start(split(cmd), { 'out_cb': function('s:on_stdout') })
  else
    " sequential run
    let s:git_root = trim(systemlist(cmd)[0])
    call s:cd_to_root()
  endif
endfunction

vimとneovimでコールバック関数の仕様が異なるので、可変長引数をいれたりタイプチェックを噛ませたりして差分を吸収しています。

Discussion