Vimはシングルスレッドである。何かが動いてる間は他のことはできない。
一方、vimには各種言語のインターフェースが付いてる。RubyやPerlやPythonからVimが操作できる。
で、これらの言語では大体スレッドが使える。ならば、これらの中でスレッド作ればVimでも非同期行けるんじゃね?ってのが今回のお話。
早速やってみた。Pythonはまともに触ったことないので変なところがあるかも知れない。突っ込み歓迎。
ちなみに試した環境は、Fedora 9 + Vim 7.2 + Python 2.5.1。
python << EOP import vim import thread import time def run(): for i in range(10): time.sleep(1) vim.command("echo " + str(i)) thread.start_new_thread(run, ()) EOP
1秒ごとにechoするだけ。これを実行すると見事に1秒ごとにechoされる。もちろん、その間Vimは操作可能。コマンドラインモードに入っても問題なし。
調子に乗って試しにこんなことして見る。
function! g:async_command() return 'javac ' . expand('%') . ' && java ' . expand('%:t:r') endfunction function! g:async_callback(res) new set buftype=nofile call append(0, split(a:res, "\n")) wincmd p redraw endfunction command! RunAsync call s:async() function! s:async() python << EOP import vim import thread import commands def run(): command = vim.eval("g:async_command()") result = commands.getoutput(command) vim.eval("g:async_callback('" + result + "')") thread.start_new_thread(run, ()) EOP endfunction
編集中のJavaファイルをコンパイルして実行して、結果を新しい窓に出す。実行中もVimの操作は可能。あくまでテストなのでエラーチェック全然してなかったり毎回出力バッファ開いたりと全体的に適当だが気にしてはいけない。
ここで使ってるcommandsモジュールはUnixオンリーなので、Windowsではこうする。
import vim import thread import nt as os def run(): command = vim.eval("g:async_command()") f = os.popen(command) result = f.read() f.close() vim.eval("g:async_callback('" + result + "')") thread.start_new_thread(run, ())
import os ってやるとなぜか見つからないって言われた。インタプリタでは行けるんだけどなぁ。
flymakeやflydiffなんかで使うと便利そうです。欠点はif_pythonが必須ってことくらいか。応用範囲は広そう。
ちなみに、最初はPerlやRubyでやったのだけどスレッドの後処理の当たりでエラー出てどうしようもなかったので慣れないPythonで試したらできたという落ちです。Perl,Rubyでできた方いたら教えてください。