Pythonでvim pluginを書く

  • 32
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

やりたいこと

  • vimのプラグイン作りたい

  • でもvimscriptわからん!!

  • Pythonで書きたい!!!!

つくるもの

  • MyNameIs <name> と入力すると、Hello <name>と表示される

  • 実装はpythonに逃がして、vimscriptはpythonラッパーのような形で使う

VimScriptでのPython

よく使うのは、pythonpyfile

pythonとpyfileは本質的には同じで、ソースが直書きされているかファイルに分離されているかの違い。たとえば、次の2つのコードは同じ

python:

python print('Hello')

pyfile

pyfile hello.py
hello.py
print('Hello')

pythonと書いたらその行は既にPythonの世界であることに注意する。次のように書いて随分と悩んだ。

function! hello#hello(name)
  python hello_hello(a:name)
endfuncion

これが間違いであることは、次のように書き換えてみるとわかりやすい

function! hello#hello(name)
  python << endpython
hello(a:name)
endpython
endfuncion

また、pydo ものもある。pydo <body>と書くと、

def _vim_pydo(line, linenr):
  <body>

という関数が作られ、選択範囲に対して1行ごとに、lineに行の内容が、linenrに行番号が渡されて呼び出される。値を返すとその値で行が置換されるが、関数なので当然returnと書かなければいけない。なのでちょっと長くなってダサい。こういうのはrubyのほうが綺麗に書けそう。

例えば、下のコードを実行すれば選択範囲の行頭に行番号が挿入される

pydo return str(linenr) + line 

実装

ディレクトリ構成

plugin/
  hello.vim
autoload/
  hello.vim
src/
  hello.py

hello.py

vimとか気にせずに、関数を作る。

1/25 追記:
名前が衝突する可能性について@thnkaさんにご指摘いただきました。グローバル空間を汚染するため、プレフィックスをつけたりクラスの中に入れたりする必要があります。

src/hello.py
def hello_hello(name):
  print('Hello {0}'.format(name))

autoload/hello.vim

ここが肝

やることは3つ。

  1. 作ったpythonスクリプトを読み込む。
  2. pythonないでvimをインポートする
  3. pluginから呼び出される関数を作り、関数内でpythonの関数を叩く

まず、作ったpythonスクリプトを読み込む。スクリプトのパスの取得方法に注意。

autoload/hello.vim
pyfile <sfile>:h:h/src/hello.py

次に、vimをインポートする。これをやらないと値のやり取りができない。

autoload/hello.vim
python import vim

最後に、plugin側から呼び出される関数を作り、関数内でpythonの関数を叩く。pythonと欠いた行は既にpythonの世界なので、vimモジュールを通して引数を渡す。

autoload/hello.vim
function! hello#hello(name)
  python hello(vim.eval('a:name'))
endfunction

plugin/hello.vim

ここは普通のプラグインとなにも変わらず。autoloadに記述した関数と、コマンドの橋渡し

plugin/hello.vim
command! -nargs=1 MyNameIs call hello#hello(<f-args>)

完成形

src/hello.py
def hello_hello(name):
  print('Hello {0}'.format(name))
autoload/hello.vim
let s:save_cpo = &cpo
set cpo&vim

pyfile <sfile>:h:h/src/hello.py
python import vim

function! hello#hello(name)
  python hello_hello(vim.eval('a:name'))
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo
plugin/hello.vim
if exists("g:loaded_hello")
  finish
endif
let g:loaded_hello = 1

let s:save_cpo = &cpo
set cpo&vim

command! -nargs=1 MyNameIs call hello#hello(<f-args>)

let &cpo = s:save_cpo
unlet s:save_cpo

参考

その他

実際に作ったのはhttpstatus.vimというプラグイン。VimからHTTPステータスコードがどんな意味だったか確認できる。テキストはPythonのBaseHTTPServer.BaseHTTPRequestHandler.responsesを使用している。

意気揚々と作ってしまったが、どうやらmattnさんが既に作っていたらしい。さらに、今回作ったものよりも高機能っぽい(ステータスコードだけじゃなくてメッセージでも調べられる)けど、どうせ私はステータスコードからしか調べないので十分なのでした。