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

bonar note

京都のエンジニア bonar の技術的なことや技術的でない日常のブログです。

vim server 入門

vim perl

vim server mode

あまり知られてないのですが、vim には server として動作し、外部から入力を受けるモードが存在します。純粋なエディタとしてだけではなく、外部からの指示で特定のファイルを開いたり、用意してる関数を実行したりってことが出来るのです。

僕も今までまったく使った事がなくて、vimscriptを単体のファイルとして実行したいなと漠然と調べていた際に見つけました。有効活用してる例ってあるのかな。。

下ごしらえ

vim server を使用するためには、そのバイナリが +clientserver というフラグ付きでconfigureされている必要があります。そして多くの場合この機能はデフォルトではありません。コンパイルオプションを調べるにはvimの起動後に

:version

でずらっと表示されます。

:version
VIM - Vi IMproved 7.2 (2008 Aug 9, compiled Nov 11 2008 17:20:43)
Included patches: 1-22
Compiled by _www@b77.apple.com
Normal version without GUI.  Features included (+) or not (-):
-arabic +autocmd -balloon_eval -browse +builtin_terms +byte_offset +cindent -clientserver -clipboard +cmdline_compl +cmdline_hist +cmdline_info +comments 
+cryptv +cscope +cursorshape +dialog_con +diff +digraphs -dnd -ebcdic -emacs_tags +eval +ex_extra +extra_search -farsi +file_in_path +find_in_path +float 
+folding -footer +fork() -gettext -hangul_input +iconv +insert_expand +jumplist -keymap -langmap +libcall +linebreak +lispindent +listcmds +localmap +menu
 +mksession +modify_fname +mouse -mouseshape -mouse_dec -mouse_gpm -mouse_jsbterm -mouse_netterm -mouse_sysmouse +mouse_xterm +multi_byte +multi_lang 
-mzscheme -netbeans_intg -osfiletype +path_extra -perl +postscript +printer -profile -python +quickfix +reltime -rightleft -ruby +scrollbind -signs 
+smartindent -sniff +statusline -sun_workshop +syntax +tag_binary +tag_old_static -tag_any_white -tcl +terminfo +termresponse +textobjects +title -toolbar
 +user_commands +vertsplit +virtualedit +visual +visualextra +viminfo +vreplace +wildignore +wildmenu +windows +writebackup -X11 -xfontset -xim -xsmp 
-xterm_clipboard -xterm_save 
   system vimrc file: "$VIM/vimrc"
     user vimrc file: "$HOME/.vimrc"
      user exrc file: "$HOME/.exrc"
  fall-back for $VIM: "/usr/share/vim"
Compilation: 
gcc -c -I. -D_FORTIFY_SOURCE=0 -Iproto -DHAVE_CONFIG_H     -arch i386 -arch ppc -g -Os -pipe -mdynamic-no-pic -arch i386 -arch ppc -pipe        
Linking: gcc   -arch i386 -arch ppc             -o vim       -lm -lncurses  -liconv        

このように -clientserver と出た場合には vim server 関連の機能が組み込まれていないので、コンパイルし直す必要があります。最新版のソースを取ってきてconfigure にはお好みのもの(--enable-multibyteとか)を入れつつ、+clientserver を付けます。

$ wget ftp://ftp.vim.org/pub/vim/unix/vim-7.2.tar.bz2
$ tar jxvf vim-7.2.tar.bz2 
$ cd vim72/
$ ./configure +clientserver
$ make
$ make install

基本的な使い方

起動

まずはサーバを立ち上げます。方法は簡単で、そのサーバを識別する文字列を付けてvimを起動するだけです。

$ vim --servername FOO

これで FOO という名前で認識されるサーバが立ち上がります。とはいえ見た目には普通にvimが立ち上がったように見えますが、

:echo v:servername

とするとFOOと表示され、servernameとして設定されていることがわかります。

このservernameはちょっと注意が必要な点があり、与えた名前が upper case に変換される(foo, Foo, FoO のどれでもFOOになる)という点と、同じ名前で2個立ち上げようとすると、2個めはFOO2となる点です。特に後者は結構直感に反する動きな気もしなくはないですね。

以下のコマンドで起動中のサーバ一覧を得る事が出来ます。

$ vim --serverlist
FOO
--remote

このFOOサーバに何か仕事をさせるにはいくつかの方法があり、例えば特定のファイルを開かせる場合には --remote を使用します。

$ perl -v > /tmp/perl.txt
$ vim --servername FOO --remote /tmp/perl.txt 

こうすると、すでに起動中のvim(FOO)の中にいきなり/tmp/perl.txtが開かれます。なかなかテキストだと伝わりにくいのですが、特定のttyに開かれたvimの内部状態を外側から変更するっていうのは見た目にキャッチーです。

ここで --remote の代わりに --remote-wait を使用すると、開いたファイルが展開されているバッファがなくなるまで(:q とか :bd とか)クライアント側は待つようになります。

--remote-expr

また、ファイルを開くだけでなく、--remote-expr を使うと特定の式を送り込んで、その評価結果をstdoutから受け取ることが出来ます。

たとえばさっきファイルを送り込んだサーバから特定の行を取り出したりする場合は以下のような感じです。

$ vim --servername FOO --remote-expr 'getline(6,9)'
Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on

計算も出来ちゃいます。

$ vim --servername FOO --remote-expr '60 * 60 * 24'
86400
--remote-send

vim の通常コマンドやvimscript関数の実行等を行う事も出来ます。--remote-send でコマンドをそのまま送りつけます。
例えば立ち上げたFOOサーバを終了させる時は以下のようにします。

$ vim --servername FOO --remote-send '<C-\><C-N>:q!<CR>'

というのがちょっと奇妙ですが、これは vim の normal mode に移行するコマンドです。現在のサーバ側の状態がわからないので、exコマンドの実行等をする場合にはこれを先頭に付けてnormal mode であることを保証する必要があります。

ただしこの場合実行結果の出力を受け取る事はできません。vimscript内でechoしたとしてもそれはstdoutに出る訳ではないのです。

VimRemote::Agent

というわけで、これを使って色々な事をやってみようと思っていて、--remote-* みたいなコマンドをつらつら書くのも大変なので、Perlのコマンドwrapperを書いてみました。
こんな感じで使えます。

    use VimRemote::Agent;

    my $agent = VimRemote::Agent->new();

    # check compile option
    if (!$agent->has('clientserver')) {
        die "configure vim with +clientserver flag";
    }

    # get running server list
    my @server_name = $agent->serverlist();

    # start new server
    my $server_name = 'NEWSVR';
    if (!$agent->start_server($server_name)) {
        die "starting server $server_name failed.";
    }

    # calc on remote server
    my $result = $agent->remote_expr($server_name, '1 + 1');
    print $result; # 2

    # send ex command to remote server
    $agent->remote_send('e /tmp/hoge.txt');

    # shutdown server
    $agent->shutdown_server($server_name);

git repo:
http://github.com/bonar/vimremote-agent/tree/master

中身はvimコマンドを叩きまくっているのをそれっぽく見せてるだけなので、無理矢理感が隠しきれてない感じになっています。
注意点

  • 実装されていないコマンド(*-wait, *-tab 系)がたくさんあります
  • -t な環境で make test すると gvim が一杯立ち上がります
  • mac os x 以外の環境でtestしてないので多分通らないです

本当はXSでかっこ良く操作したかったのですが、if_xcmdsrv.c とかを観た感じちょっと僕の実力では難しそうですね。。OS毎に実装も違うだろうし。。

まとめ

エラそうに書きましたが、基本 :help remote に全部書いてあります。どういう局面で役に立つのか、それが一番悩ましいのですが、

  • IRCサーバとかが作れる
  • システムログとかをvim server に送るようにして、みんなでそれを共有できる
  • vnewして右側がlogtailになっている的なものとか
  • 頑張れば遠隔操作とかが出来るようになって、外科手術をみんなで観る的な公開コーディングが出来るかも

みたいな夢が広がります。なんでそれをvimで、っていうのは、言わない約束じゃないですか。