RoRの15分ムービーで使われているTextMateのsnippetsもどきなvimプラグインsnippetsEmu

Ruby on Rails の15分ムービーで使われている、ハイテクないまどきのOSXのGUIのエディタ TextMate の補完機能はいいなーとおもっていたところで おぎろぐはてな – (PHP)プログラマのためのVIM (11) – PHP向けカスタマイズ(3) Andreiがつかってるプラグイン(Andreiはvimを作っているひとです)の中に

TextMate(mac用のエディタ)のSnippets機能のいくらかをエミュレートしてくれます

というのがあるのを見つけて、試してみなければと思いつつ放置していたのをようやく試しました。英語でも日本語でもあんまり情報がなくて、動くようになるまで苦労しました。

なにができるか
いろんなモードでインテリジェントないろんな補完が可能です。
とりあえずHTMLモードの場合
doc4t<TAB>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

に展開されたり、script<TAB>

<script type="text/javascript" language="<javascript>" charset="">
// <![CDATA[
<{}>
// ]]>
</script>

に展開されたりします。

そんな単純な展開だけでなく、textarea<TAB>

<textarea name="_" rows="<{}>" cols="<{}>"><{}></textarea>

と補完されて、さらにname=""の部分にカーソルが移動します。nameを入力してTABを押すと、次はrowsの入力になり、もう一度押すとcolsの入力になって、もう一度押すと</textarea>の後ろにカーソルが移動してくれるので、ちょっと楽ができます。perlであればclass<TAB>で、パッケージのコンストラクタに展開してくれたり、というようなのが言語別にはじめからいくつか定義されていて、もちろん自分で定義することもできます。

自分で定義するときは、展開ルールを vim script で細かく記述できるので、入力された値を加工して出力する、ということもできます。(vim scriptが書ければですけど)

インストール
snippetsEmu – An attempt to emulate TextMate’s snippet expansion : vim online から version 1.1 をダウンロードしてきます。
拡張子が .vba なのでWindows専用?と思ったのですが、これは VimBAll の略だそうで、この拡張子のプラグインは、

vim snippetsEmu.vba
:source %

でインストールすることができます。はじめて知りました。
そうそう、snippetsEmuはvim7でないと動かないらしいので、いまどき古いvimを使っていたら新しいのにかえる必要があります。

.vimrcの設定
デフォルトではTABで補完されるようになっています。
もとのAndreiの資料では”TABだとうざいからリマップしようね”って書いてあるけど、書いてある通りに

if exists('loaded_snippet')
    imap <C-B> <Plug>Jumper
endif

するとなぜか^Bが入力されるだけなのでTABのままで使っています。

はじめ、いくらTABを押しても何の反応もなくて困っていたら ~/vim/doc/snippets_emu.txt

SNIPPETSEMU TROUBLESHOOTING                     *snippets_emu-troubleshooting*

Problem:    Bundles are not loading.
Answer:     Ensure that you have filetype plugins turned on. Include the
            following in your vimrc: <>

                filetype plugin on

と書かれていました。
なので、書いてない場合 .vimrc に

filetype plugin on

を付け加えます。もっと目立つところに書いてほしかった….

snippetsEmu.vimの修正
TABを押すと確かに補完されるようになったものの、はじめに入れた展開用のキーワードが削除されないまま残った状態になりました。doc4t<TAB>と入力すると

doc4t<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

になっちゃいます。

これだとめっちゃ不便なので、読めないながらも vim script を追ったところ
snippetsEmu.vim の

    " if this is a mapping, then erase the previous part of the map
    " by also returning a number of backspaces.
    let bkspc = substitute(origword, '.', "\<bs>", "g")
    call <SID>Debug("Snippy: Backspacing ".<SID>StrLen(origword)." characters")

の部分で bkspc に、キーワードの文字数ぶんBackSpaceが入って

 return "\<Esc>:set paste\<CR>a".bkspc.delEndTag.initial.rhs."\<Esc>:set nopaste\<CR>?".initial."\<CR>".strlen(initial)."xi\<C-r>=<SNR>".s:SID()."_NextHop()\<CR>"

でキーワードが文字数ぶん戻ってキーワードが消えるようになっているはずなのですが、なぜか消えていないのがわかりました。

正しく書き直すには知識がなさすぎるのでとりあえず

@@ -773,7 +773,7 @@
     let rhs = substitute(rhs, "\<CR>", "&".tab_text,'g')

     call <SID>Debug("Snippy: ---------------- End of Jumper ----------------")
-    return "\<Esc>:set paste\<CR>a".bkspc.delEndTag.initial.rhs."\<Esc>:set nopaste\<CR>?".initial."\<CR>".strlen(initial)."xi\<C-r>=<SNR>".s:SID()."_NextHop()\<CR>"
+    return "\<Esc>:set paste\<CR>bcw".delEndTag.initial.rhs."\<Esc>:set nopaste\<CR>?".initial."\<CR>".strlen(initial)."xi\<C-r>=<SNR>".s:SID()."_NextHop()\<CR>"
   else
     " No definition so let's check to see whether we're in a tag
     if <SID>CheckForInTag()

にしたら、補完時にキーワードがちゃんと消えるようになりました。

というわけで vim7ユーザ定義補完関数を使ってみる でうまくいかなくてあきらめていたvimでの任意文字列補完ができるようになったのでうれしいです。


About this entry