読者です 読者をやめる 読者になる 読者になる

一生貴方についてゆきます!

外部スクリプトを使用しないpythonによるフィルタリング処理 - サイト更新停滞ちうっ

pythonを利用したフィルタリングをしたい。でも、使い捨ての処理に外部ファイルをいちいち用意するのは面倒。そんな人向け。

むきゃー。まさにこんなことができるエディタが欲しかったのです!VimPythonスクリプトを使えることは知っていたけど、コマンドモードから直接Pythonを使って中身のテキストをいじれるとは知らなかった。ありがたやありがたや。
参考:VimのPythonインターフェース

しかしフィルタ処理をするときにcurrent.rangeほげほげと書くのは長ったらしく感じたので、いくつかのショートカットと共に_vimrcに追加してみた。

python << END
import sys, vim
CW = lambda x=0: getattr(vim.current,"window")
CB = lambda x=0: getattr(vim.current,"buffer")
CR = lambda x=0: getattr(vim.current,"range")
CL = lambda x=0: getattr(vim.current,"line")
END

本当は下のように書きたかったし、当然これで動くものだと思っていたけどこれじゃちゃんと動いてくれないのだ。

python << END
import sys, vim
CW = vim.current.window
CB = vim.current.buffer
CR = vim.current.range
CL = vim.current.line
END

範囲を編集しようとしてCRを参照しても何も選択してないようなふるまいをする。何かがおかしいと思って次のようにrangeオブジェクトを調べてみたところ(pythonコマンドはpyと省略できる)……

:py print id(vim.current.range)

毎回オブジェクトのIDが変わってるではないか!!!
オブジェクトのIDが変わっていると言うことは毎回新しいオブジェクトが生成されているということ。つまり、起動時に一度だけ実行される_vimrcに「CRにcurrent.rangeオブジェクトを保存する処理」を書いておいても、実際に選択範囲のテキストを操作したいと思ってpyコマンドを実行したときには、現在の選択範囲を反映していない古いオブジェクトしか残っていないのだ。

その後autocmdでどうにかならないもんかと様々なイベントを試してみたけど、どうにもならなかった。「コマンドモードに移行したとき」っていうトリガは無いんだね。

結局、関数呼び出しで動的にオブジェクトを取得する方法に落ち着いた。かっこがとてもお邪魔虫なのだけど、Rubyみたいにかっこ無しで関数呼び出しはできないので諦める。

下の三行に行番号を付けたいときなんかに

foo
bar
hoge

ビジュアルモードで三行を選択して下のようなコマンドを打つと

:'<,'>py CR()[:] = ["%d %s" % (i,CR()[i]) for i in RR()]
0 foo
1 bar
2 hoge

とできる。そのままPythonが使えるので余計なこと(Vimスクリプトあれこれ)を覚えなくて済むのと外部で別のファイルを作成しなくて済むのがありがたい。

おまけ

かっこを無くすにはpropertyを使う方法しか思いつかない。

python << END
import sys, vim
class _VimVar(object):
  W = property(lambda self: vim.current.window);
  B = property(lambda self: vim.current.buffer);
  R = property(lambda self: vim.current.range);
  L = property(lambda self: vim.current.line)
class _RangeVar(object):
  S = property(lambda self: vim.current.range.start);
  E = property(lambda self: vim.current.range.end);
  L = property(lambda self: len(vim.current.range));
  R = property(lambda self: range(len(vim.current.range)))
C = _VimVar()
R = _RangeVar()
END

そうするとさっきのコマンドは次のように書ける。

:'<,'>py C.R[:] = ["%d %s" % (i,C.R[i]) for i in R.R]

まぁたいして変わらないけど見た目はこっちの方がいいかな。