Hatena::Groupvim

selfの外部変数名の取得

selfの外部変数名の取得

Dictionary function を autocmd で利用する場合などで、self の外部から見える変数名を知りたいことがある。

実装例

let s:Base = {}

function! g:Base.to_str()
  for [namespace, prefix] in [[s:, 's:'], [g:, 'g:']]  " [b:, 'b:'], [w:, 'w:'], [t:, 't:'], [v: 'v:'], [l: 'l:']
    for var_name in keys(namespace)
      if type(self) == type(namespace[var_name]) && self == namespace[var_name]
        return prefix . var_name
      endif
    endfor
  endfor
  throw 'ERROR: to_str()'
endfunction

let s:Deriv = copy(s:Base)

function! s:Deriv.echo()
  echo 'My name is "' . self.to_str() . '"!'
endfunction

call s:Deriv.echo()
  • 変数のスコープについて、selfと同一オブジェクトを保持する変数を探し、その名前を返すことで実装している。スコープのプレフィックスg:等は、それ自体で該当スコープの変数の全てを保持するDictionaryを表すので、これを利用している。
    • 上記の実装ではs:/g:のみしか調べていない。b:/w:/t:については調べる必要性のある場面は少ないだろうし、v:/l:については調べてもまず意味がないからだ。特にl:はl:selfが必ずマッチする。
    • 厳密に言えばVim scriptから全ての変数を列挙することは不可能である。特にt:/s:に関してはVim scriptから現在可視なタブページやスクリプト以外のローカルスコープを参照することはできない(前者については将来的にgettabvar()のような関数が標準で用意されるとは思われるので対応できるようにはなるだろう)。
  • この方法では複数の変数が同一オブジェクトを保持していた場合にどれか一つの名前だけが返される。該当する変数名を全て列挙したい場合や、一つだけで十分だが優先して探したいスコープの順序が決まっている場合は適宜修正する必要がある。
  • :letの出力を(:redirで保存して)解析することでも実装できる。ただg:等を用いる方が様々な点で便利である。

実行結果

My name is "s:Deriv"!

autocmd で Dictionary function を利用する例

execute printf('autocmd BufDelete call %s.on_buf_delete()', self.to_str())

BUGS

  • Dictionaryが他のDictionaryやListに格納されており、他の変数から直接参照することができない場合、変数名を得ることはできない。