sci

最果て風呂

続 Vim のウインドウ変数とバッファ変数と

ウインドウ変数について

これは下記の記事から続いているお話。

Vim のウインドウ変数等について調べたのだが...
Vim の変数のスコープについて調べたら「わかってない」ということがわかった

thinca さんの記事を読んで「自分が現在どこに居るのか」を常に意識するようにした。これは山でも、職場でも、地域でも同じことが言えるのだけど、プログラミングでは意識していなかった。

今まで作成してきたスクリプトは 1 ファイルで完結するものばかりだったし、ターミナル上で実行したらすぐに終了してしまうユーティリティ類ばかりなので、グローバル変数を使いまくりなのだった。

Emacs は立ち上げっぱなしにする使い方だけれども、関数や変数は「およそ重複しないであろうと思われる」名前を付けて、ほとんどがグローバルで使っている。Vim はシェルの中で移動しながら起動・終了を繰り返すという使い方なので、やはり適当に付けた名前やスコープの変数を使っていた。

夜と朝に時間を置いて 3 回読み直してみた。何となくわかったような、わからないような、モヤモヤした感覚。もう少しで「こういうことなのか!」という目覚めが起きそうな状態。例えるなら「咽に刺さった骨」みたいな。

解説にあるようにスクリプトファイルを用いないでコマンドラインモードからチビチビと入力して確認をする。例ではバッファ変数になっていたが、今までずっとウインドウ変数について考えていたので w: でやってみる。ひとつのプロセスを確認する度に Vim は再起動している。(:echo tabpagenr() は常に 1)

let w:homu = 10
echo winnr()
;=> 1
echo w:homu
;=> 10

予想通りの挙動だった。

let w:homu = 10
new
echo winnr()
;=> 1
echo w:homu
;=> E121: Undefined variable: w:homu
    E15: Invalid expression: w:homu
C-w j (let w:homu = 10 を実行したウインドウに移動。画面は二分割されたまま)
echo winnr()
;=> 2
echo w:homu
;=> 10vim

最初の結果が「エラーになる」こと、ウインドウを移動すれば「10 が表示される」ことは予想通り。「自分が let w:homu = 10 を実行したウインドウに居る場合にのみ w:homu へアクセスすることが出来る」ということ。

私が混乱した原因はウインドウ番号との関係だ。ウインドウが増えるにつれて、最初に開いたウインドウ(つまり let w:homu = 10 を実行したウインドウ)の番号がどんどんと変化していくのである。次にウインドウの数を増やした例をやってみる。

let w:homu = 10
new
new
echo winnr()
;=> 1 (三分割された画面の一番上)
echo w:homu
;=> E121: Undefined variable: w:homu
    E15: Invalid expression: w:homu
C-w j
echo winnr()
;=> 2 (三分割された画面の真ん中)
echo w:homu
;=> E121: Undefined variable: w:homu
    E15: Invalid expression: w:homu
C-w j
echo winnr()
;=> 3 (let w:homu = 10 を実行したウインドウに戻った。三分割された画面の一番下)
echo w:homu
;=> 10
C-w o (他のウインドウを消して let w:homu = 10 を実行したウインドウのみにする)
;=> 1
echo w:homu
;=> 10

つまりこの結果から言えることは「ウインドウ変数とウインドウ番号は強く結び付けられたものではない(一意ではない)」ということ。もし一意であるならば、最後の 2 例で echo winnr() の値が 3 と 1 と異っているにもかかわらず、エラーとならずに 10 という同じ結果を得ることが出来るはずがないではないか。また、そもそもウインドウ番号が変化するという仕様を採用することはできない(一意とするならば)。3 日間ほど悩んでいたが、この結論でいいのだろうか?

ウインドウを閉じればその変数は無くなるし、アクセスすることもできなくなる。これは良い。ただ、ウインドウを増減させた後に目的のウインドウ変数にアクセスするためにはどうすれば良いのだろうか?上述したようにウインドウ番号とは一意ではない関係なのだから。そもそもウインドウ変数は何と束縛されているのだろう?裏にウインドウネームみたいなものでもあるのかな。

私の Vim の使い方は一画面 1 ウインドウ、たまにヘルプで二分割されるくらいなので、タブ変数とウインドウ変数については考えないことにしよう。使いもしない事に時間を取られていては本丸である b:, s: 変数にいつまで経っても辿り着けない。

バッファ変数について

最終的な目標は s: 変数の攻略にあるのだが、その前に b: 変数について調べる。これは自分も実際に使っていて、色付けするキーワードを保持するための変数に付けている。ウインドウとは異なり、バッファに対しては一意に番号が振られるようなので、理解はし易いかな?

バッファ名がすべて「No Name」になっているとこんがらがるので、test1 ... のような空のファイルを用意して、test1 から順番に開いていくことにする(vim text* ではやらない)。次のような感じ。

e test1
e test2
e test3
buffers
;=> 1      "test1"                        line 1
    2 #    "test2"                        line 1
    3 %a   "test3"                        line 1

さっそく実験してみる。

e test1
e test2
e test3
bnext (test1 に移動)
let b:homu = 10 (text1 バッファの変数 b:homu に 10 をセット)
echo b:homu
;=> 10
bnext (text2 に移動)
echo b:homu
;=> E121: Undefined variable: b:homu
    E15: Invalid expression: b:homu
bnext (text3 に移動)
echo b:homu
;=> E121: Undefined variable: b:homu
    E15: Invalid expression: b:homu

予想した通りの結果になった。次は少しバッファ変数を増やしてみる。

e test1
let b:homu = 10
e test2
let b:homu = 20
e test3
let b:homu = 30
bnext (test1 に移動)
echo b:homu
;=> 10
bnext (text2 に移動)
echo b:homu
;=> 20
bnext (text3 に移動)
echo b:homu
;=> 30

これも予想した通りの結果になった。Vim の内部でどのように保持されているのかわからないけれども、使う側からすれば test1-b:homu = 10 のようになっていると考えれば良さそう。つまり現在自分の居るバッファ名がプレフィクスに付く感じ。

この考えでいくと、例えば test3 バッファを開いている状態で b:homu にアクセスすると、test3-b:homu を参照して 30 を得るという形になる。test3 で b:homu を設定していなければ当然のことながら「そんな変数シラネ」となりエラーが返ってくる。代りに test2 や test1 で設定した b:homu が返ってくることはない。

ここで thinca さんの記事にあった別のスコープの変数を取得する実験をしてみる。バッファ変数を得るには「getbufvar(バッファ番号,変数名)」を使うとのこと。早速上の例でやってみよう。

命題は text1 バッファ上で、text3 のバッファ変数を取得すること(すでに text1 バッファに移動していることとする)

echo b:homu (自分が現在居るバッファ変数 b:homu 値を得る)
;=> 10
echo getbufvar(3, g:homu) (text3 のバッファ変数 b:homu 値を得る)
;=> 何も表示されない(エラーにもならない)

help を見ると、頭文字 b: を付けてはいけないと書いてある。

echo getbufvar(3, homu) 
;=> E121: Undefined variable: homu
    E116: Invalid arguments for function getbufvar(3, homu)
    E15: Invalid expression: getbufvar(3, homu)

???、例題を見るとダブルクォーテーションマークで括るみたい。

echo getbufvar(3, "homu") 
;=> 30

よっしゃ〜!シングルクォーテーションマークで括っても大丈夫みたい。ここで注目すべき点は、他のバッファ変数を得るためにはわざわざ関数を使って取得しなければならないこと(しかも b: 無しで)。目的意識を持って他のバッファ変数を「取得しようとしなければならない」わけで、つまり同じ変数名であったとしても意図せず他のバッファ変数を得てしまうことがない。

これらの関数は便利そうだけど、実際にはどういった状況で使いたくなるのだろう?想像が付かない。

コマンドラインから直接入力する方式でのウインドウ変数の実験は終了した。スクリプトファイルから読み込んで(実行して)の調査は、また別の機会に実験することにしよう。次はいよいよ s: 変数に突撃と行きたいところだけど、少し休憩。