lazy_importとインテリセンス
昨日に引き続き、微妙にVim関連のネタです。
最近は、PythonやRubyみたいなLLでもちゃんとインテリセンスで補完できるようなIDEが増えてきました。
僕も実年齢はともかく精神的にはゆとりなので、インテリセンスが無いとまともにコードを書ける気がしません。
しかし、Bazaarのソースでは"lazy_import"という関数が多用されていて、こいつが補完のジャマをします。
lazy_import
lazy_importというのは、こんなやつです。
from bzrlib.lazy_import import lazy_import lazy_import(globals(), """ import os, re from bzrlib.workingtree import WorkingTree from bzrlib.plugins.qbzr.lib.util import ( get_set_tabstop_width, QBzrWindow, ) """)
lazy_importの中に書かれたimportは、その場では実行されず、本当にそのモジュールが必要となるまでimportが遅延されます。bzrコマンドの起動を速くするための仕組みですね。
これはこれで便利なんですが、当然のことながらIDEはこれがimport文であるとは認識してくれないので、これらのモジュール内のクラスや関数を呼び出そうとしてもちゃんと補完してくれません。
コレを何とかできないかと思って、VimのPython用補完プラグインであるpythoncomplete.vimをいじってみました。
pythoncomplete.vim
pythoncomplete.vimの中を見てみると、vimcomplete functionの中で、バッファからソースコードを取得しています。
ここで、取得したソースコードの中のlazy_import呼び出しを普通のimportに書き換えてしまえば、補完が効くようになりそうな気がします。
def vimcomplete(context,match): global debugstmts debugstmts = [] try: import vim def complsort(x,y): # ・・・(略)・・・ cmpl = Completer() cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')")) all = cmpl.get_completions(context,match) all.sort(complsort) # ・・・(略)・・・ except vim.error: dbg("VIM Error: %s" % vim.error)
で、やってみました。正規表現を使って、「lazy_import("""」と「""")」を取り除いています。
別に完璧である必要はないので、正規表現はかなりいいかげんです。
--- ./pythoncomplete.vim.orig Thu Dec 15 06:37:36 2011 +++ ./vimfiles/bundle/pythoncomplete/plugin/pythoncomplete.vim Thu Dec 15 22:59:31 2011 @@ -96,6 +96,20 @@ def showdbg(): for d in debugstmts: print "DBG: %s " % d +import re +re_lazy_import = re.compile(ur''' +^lazy_import\s*\(globals\(\)\s*,\s*("""|\'\'\') # lazy_import(globals(), """ +(?P<contents>[^"\']*) # inside here document +\1\s*\) # """) +''', re.M|re.X) +re_indent = re.compile(ur'^[ \t]+', re.M) + +def strip_lazyimport(source): + def unindent(text): + return re_indent.sub('', text) + + return re_lazy_import.sub(lambda m:unindent(m.group('contents')), source) + def vimcomplete(context,match): global debugstmts debugstmts = [] @@ -121,7 +135,7 @@ except: return 0 cmpl = Completer() - cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')")) + cmpl.evalsource(strip_lazyimport('\n'.join(vim.current.buffer)),vim.eval("line('.')")) all = cmpl.get_completions(context,match) all.sort(complsort) dictstr = '['
何かもっといい方法があるような気もしますが、しばらくこれでやってみようと思います。
別のアプローチ
もっと単純な方法としては、lazy_importの呼び出しをこんな風に書き換えちゃうようなVimプラグインなりを作るという手もありますね。pre_commitフックで"##UNCOMITTABLE##"という文字列が含まれるファイルのコミットは拒否するようにして。
##UNCOMITTABLE## lazy_import(globals(), """ import os, re from bzrlib.workingtree import WorkingTree from bzrlib.plugins.qbzr.lib.util import ( get_set_tabstop_width, QBzrWindow, ) ##UNCOMITTABLE## """)
ところで、neocomplcacheはすばらしいですね。