Vim script の変数のスコープについてのお話

Vim の変数のスコープについて調べたら「わかってない」ということがわかった - sci
このような記事を見かけた。具体的にどんなコードで試したのかわらないので具体的なアドバイスはできそうにないが、せっかくので Vim script の変数のスコープについて解説してみるよ。

どんなスコープがあるのか

Vim の変数は変数名の接頭子によってスコープが決定します。

接頭子 スコープ
g: グローバル
b: 現在のバッファにローカル
w: 現在のウィンドウにローカル
t: 現在のタブページにローカル
s: 現在のスクリプトファイルにローカル
l: 関数ローカル
v: 組み込み変数

接頭子を省略した場合は、

  1. 関数内だった場合は l:
  2. それ以外ならば g:

になります。ただし、count などの一部の変数は互換性の関係で v:count のように組み込み変数として扱われてしまうので注意が必要です。
当然ですが、バッファ/ウィンドウ/タブページが何であるかはきちんと知っておく必要があります。長くなるのでここでは解説しません。

今どこにいるのかを意識する

Vim script を書く上で重要なのは、スクリプトの実行中に今どこにいるのかを意識することです。上記のスコープの説明を見るとわかるように、各ローカル変数は現在のコンテキストに依存します。例えば、

let b:foo = 10
new
echo b:foo

これは、b:foo が存在しないというエラーになります。なぜなら、2行目で新しいバッファを開いているので、最初の b:foo と最後の b:foo は別のバッファの変数を参照しているからです。
これは変数に限らずローカルオプションやローカルなキーマッピングなどにも言えることなので、スクリプトの各行で Vim がどのような状態にあるかを意識すると良いです。
また、例えば以下のようなコード

for i in g:user_config
  call s:setting(i)
endfor

これをグローバルスコープ(関数の外)に書いていた場合、最初に書いたルールの通り、i はグローバル変数になります。これは見落としがちなので注意が必要です。

別のスコープの変数にアクセスする

となりのバッファの変数を操作するのに、いちいちバッファを移動するのは面倒です。そもそも、バッファの移動は様々な autocmd が走ったりして副作用が大きいため避けたいところです。そんなときのために、別のスコープの変数にアクセスする手段がいくつか用意されています。

バッファ変数

getbufvar({バッファ番号}, {変数名}) で指定バッファの変数の値を取得できます。
setbufvar({バッファ番号}, {変数名}, {値}) で指定バッファの変数の値を設定できます。

タブページ変数

gettabvar({タブ番号}, {変数名}) で指定タブページの変数の値を取得できます。
settabvar({タブ番号}, {変数名}, {値}) で指定タブページの変数の値を設定できます。

ウィンドウ変数

gettabwinvar({タブ番号}, {ウィンドウ番号}, {変数名}) で指定タブページの指定ウィンドウの変数の値を取得できます。
settabwinvar({タブ番号}, {ウィンドウ番号}, {変数名}, {値}) で指定タブページの変数の指定ウィンドウの値を設定できます。

参考資料

どうでもいいこと

はてダで g:vim とか書くとはてなグループへのリンクになってうざいので、

[]g:vim[]

って書くと良いです。