Vim のプラグインを作る時に注意すべきことや便利なプラグイン

この記事は Vim Advent Calendar 2012 216日目の記事になります。


さて、Vim Advent Calendar 2012 の1日目にも似たような内容の記事を書いたのですが、今回はもう少し Vim script よりの内容になっています。
普段、Vimプラグインを作る際に注意している事や便利なプラグインなんかを紹介してみたいと思います。

[グローバル変数のオプションの初期化に get(g:) を使用する]

プラグインのオプションでグローバル変数を使用する場合、

" g:hoge_flag がない場合、変数を初期化する
if !exists("g:hoge_flag")
    let g:hoge_flag = 0
endif


のように exists() を使用するのではなくて

" g: は辞書なので get を使用する事が出来る
" キー "hoge_flag" を取得し、"hoge_flag" が無ければ 0 を返す
let g:hoge_flag = get(g:, "hoge_flag", 0)


のように get() を使用すると記述がすっきりとします。

[レジスタを汚さない]

例えば特定の行を削除する場合、

" バッファの3 〜 10 行間を削除する
3,10delete


のように :delete を使用します。
しかし、上記の場合ではレジスタが書き換えられてしまいます。
このような副作用を回避するために次のように _ を指定する必要があります。

3,10delete _


これでレジスタが書き換えられません。

[plugin ファイル内で :set は使用しない]

plugin ファイル内で :set を使用すると Vim 全体に影響を与えてしまいます。
その為、ユーザの意図しない設定が勝手に行われてしまいます。
このような副作用を抑えるために plugin ファイル内に限らず極力 :set を使用するのは避ける方がよいです。
また ftplugin 等でバッファに対してどうしても :set を行いたい場合は :set ではなくて :setlocal を使用するようにしましょう。

[:set したオプションは戻す]

次のように一時的にオプションを変更したい場合は、必ず元の値に戻しましょう。

" 何かしらのデータを任意の errorformat で quickfix へ出力したい場合
try
    " 現在の値を保存していく
    let errorformat = &g:errorformat

    " 任意の errorformat でパース
    let &g:errorformat = my_errorformat
    cgetexpr data
finally
    " 元に戻す
    let &g:errorformat = errorformat
endtry


上記のように :finally を使用すれば例外が発生した場合でも安全にオプションを元の値へ戻す事が出来ます。

[マッピングをキーに対して設定しない]

これも :set と同様にユーザの意図しない動作を行わないようにするためです。
(むしろ Vim の操作に直接影響するので :set 以上にたちが悪い。
これに関してはいくつか回避方法があるんですが、 を使用する方法がベターでしょうか。

" プラグイン側では J に直接処理を割り当てるのではなくて
" noremap <silent> J :call jplus#join(getchar())<CR>

" <Plug> に対して処理を割り当てる
noremap <silent> <Plug>(jplus-getchar) :call jplus#join(getchar())<CR>

" ↑↑↑プラグイン側の設定
"-----------------------------------------
" ↓↓↓ユーザ側の設定

" ユーザはその <Plug> を使用して好きなキーに処理を割り当てる
nmap <silent> J <Plug>(jplus-getchar)


こんな感じでプラグイン側は を提供するだけで、実際にどのキーで処理するのかはユーザ側が決定します。
他の回避方法としては

  • マッピングするキーに対して prefix キーを設定出来るようにする
  • マッピングを行う関数を作成して、ユーザがその関数を明示的に呼び出すようにする

などでしょうか。
ここら辺はあまり詳しくないので他にも手段があるかもしれません。

[グローバル領域の for 文に注意]

例えば、次のような for 文をグローバル領域に記述した場合、問題があります。

for i in range(10)
    " なんか処理
endfor


上記の場合、for 文で使用した i がグローバル変数として定義されてしまい for 文の後でも残ってしまいます。
この場合は、s: をつけましょう。

for s:i in range(10)
    " なんか処理
endfor

[Vim を再起動する]

プラグインを作成していると頻繁に Vim を再起動する事もあると思います。
そういう時は restart.vim を使用すると便利です。

[:echom デバッグ]

Vim script で printf デバッグを行う場合は :echom を使用すると便利です。
:echom を使用するとコマンドウィンドウへの出力後にも :messages で出力を確認する事が出来ます。
また、辞書の値を出力する場合は prettyprint.vim を使用するといい感じに整形して出力してくれるので便利です。


あと最近では vimconsole.vim というプラグインが作成されたのでそれを使用してみるのもいいと思います。

[まとめ]

プラグイン内で副作用のある設定、処理は行わないようにしましょう。
用法用量を守って正しくお使いください。