読者です 読者をやめる 読者になる 読者になる

Vim プラグインの開発スタイルのお話

vim

Vimプラグインって作ってる最中は Vim 自体の動作も不安定になりがちだし、普段の環境と同じ環境で作ってるとなにかと弊害が起きがち。ちょっとした変更ならまだしも、大規模な変更は 1 度の作業で終わらないこともあるし、作業中断して別のことをやることになったりするとさらに面倒。
または、VCSプラグインを管理したくて、プラグインごとにリポジトリを独立したいけど環境分けるのが面倒だとか。
というわけで私が普段やってる Vim プラグインの開発スタイルを晒してみようと思う。

設定

私の vimrc には最後の方に以下の記述がある。

" Load settings for each location.
augroup vimrc-local
  autocmd!
  autocmd BufNewFile,BufReadPost * call s:vimrc_local(expand('<afile>:p:h'))
augroup END

function! s:vimrc_local(loc)
  let files = findfile('vimrc_local.vim', escape(a:loc, ' ') . ';', -1)
  for i in reverse(filter(files, 'filereadable(v:val)'))
    source `=i`
  endfor
endfunction

if g:loaded_vimrc == 0
  call s:vimrc_local(getcwd())
endif

前半は Vim Hack #112 で紹介したものとまったく同じだが、その後に追加がある。
g:loaded_vimrc は単にリロードした際には実行しないようにするもので、vimrc の最後で 1 に設定される。
で、if の中身だけど、getcwd()、つまり Vim を起動したディレクトリと、その親ディレクトリにある vimrc_local.vim を読み込んでいる。

ディレクトリ構成

まず Vim プラグイン開発用のディレクトリを用意して、その下にサブディレクトリとして各プラグインのディレクトリを作っている。こんな感じ。

~/work/vim-plugins/
|-- quickrun
|   |-- autoload
|   |-- doc
|   `-- plugin
|-- ref
|   |-- autoload
|   |   |-- ku
|   |   `-- ref
|   |-- doc
|   `-- plugin
|-- visualstar
|   |-- doc
|   `-- plugin
`-- vparsec
    |-- autoload
    |-- doc
    `-- sample

プラグインはこれで全部じゃないけど、雰囲気としてはこうなってる。

キモの部分

で、ここで言う ~/work/vim-plugins には以下のような vimrc_local.vim が置いてある。

if exists('s:loaded')
  finish
endif
let s:loaded = 1
let s:basename = expand('<sfile>:h:t')
let s:dir = getcwd()
if fnamemodify(s:dir, ':h:t') ==# s:basename
  let &rtp = s:dir . ',' . &rtp . ',' . s:dir . '/after'

  augroup vimrc-local-dev-plugin
    execute 'autocmd SourcePre */' . s:basename . '/*/plugin/*.vim'
    \       'unlet! g:loaded_{expand("<afile>:p:r:s?.*/plugin/??:gs?[/\\\\]?_?")}'
  augroup END
endif

色々やってたからごちゃごちゃしてしまった感があるが、2 番目の if は最初の行以外無視していい。
何をしているかというと、現在のディレクトリを 'runtimepath' に追加している。
ここで重要なのは、これが vimrc の段階、つまりプラグインがロードされる前に実行されるということ。これによって開発中のプラグインが先にロードされる。
つまりは各開発中のプラグインのディレクトリへ移動して Vim を立ち上げれば自動的に開発中のプラグインがロードされるということ。普段使ってるのは別に通常の 'runtimepath' に入ってるけど、多重ロード防止コードがあるので読み込まれることはない。
こうやって動くかどうかも怪しいバギーな状態のプラグインと普段使うプラグインを分離している。

ついでに解説

if の後半で何をしているか。autocmd を 定義しているんだけど、これは plugin/hoge.vim を :source し直す際に多重ロード防止用の変数を unlet してる。まあ最近は autoload がメインコードの場合が多いからあまり必要はなくなってきたけど。

以上

誰かの参考になれば幸いです。