こんにちは。Sleipnir 3 開発担当兼 Sleipnir Mobile for Android 開発担当の西田です。
普段は Visual Studio と Eclipse で開発しているヘタレ Vimmer ですが、
Ruby や Python でちょっとしたスクリプトを書いたりするときは Vim を使ってます。
というわけで今回は、そういうときに役立つ Vim の小ネタを紹介します。
さて、皆さんは snipMate.vim というプラグインをご存知でしょうか。
簡単に紹介しますと、よく使うコード片 (snippet) を、それに関連付けられた短い単語 (trigger) を入力するだけで挿入することができるプラグインです。
例えば C 言語 のソースを編集中に
for<tab>
と入力すると(<tab> は タブキー)、
for(int i = 0; i < count; ++i) { }
と展開されます。ここでは「for」が trigger で、展開された for 文 が snippet になります。
まぁこれだけなら別に大したこともないのですが、snipMate.vim のすごいところは、展開後に さらに <tab> を押すことで、「int」、「i」、「count」を順に選択することができるので、必要な部分だけを簡単に書き換えることができます。もちろん書き換えずにスキップすることもできます。さらに嬉しいことに、ひとつの i を変更すると連動して残り 2 つの i も同様に変更されます!DRY ですね。最後にブレースの中にカーソルが移動するので、流れるようにコードを書き続けることができます。
他にも snippet にスクリプトを埋め込んで、展開時にそれを評価して挿入するといったこともできます。例えば、
date<tab>
と入力すると
2011-07-05
と展開されるのですが、この日付はスクリプトで今日の値が得られるようになっています。
このプラグインはインストールするだけでもれなく C, C++, java, Javascript, Objective-C, Ruby, Python, Perl, HTML その他いろいろなファイルタイプ用の snippet がたっぷり付いてくるのですぐに使い始められます。もちろん、自分で snippet を追加・削除・編集することもできます。
といった具合にもう手放せない感じの snipMate.vim ですが、若干不満に思うこともあります。それは、
- <tab> は他のことにも使うキーなので誤操作しやすい。もっとマイナーなキーで展開するようにしたい。
- snippet が増えてくると、trigger がわからなくなる。
です。というわけで次のように改造します。
- <tab> ではなく Ctrl-L で展開する。
- Ctrl-L を押したとき、マッチする trigger が存在すれば snippet を展開し、そうでなければ trigger の補完候補一覧を表示する。
こうすることで、誤操作を気にすることがなくなり、また trigger を忘れてもとりあえず Ctrl-L を押すことで trigger の一覧を表示することができるはず!
それでは、github にある最新版からちゃちゃっと hack した結果の diff はこちらです:
diff -r 6be3f9c76b17 -r f6e6c3ef4130 after/plugin/snipMate.vim --- a/after/plugin/snipMate.vim Tue Jul 05 15:44:20 2011 +0900 +++ b/after/plugin/snipMate.vim Tue Jul 05 15:46:08 2011 +0900 @@ -10,11 +10,11 @@ " " You can safely adjust these mappings to your preferences (as explained in " :help snipMate-remap). -ino <silent> <tab> <c-r>=TriggerSnippet()<cr> -snor <silent> <tab> <esc>i<right><c-r>=TriggerSnippet()<cr> -ino <silent> <s-tab> <c-r>=BackwardsSnippet()<cr> -snor <silent> <s-tab> <esc>i<right><c-r>=BackwardsSnippet()<cr> -ino <silent> <c-r><tab> <c-r>=ShowAvailableSnips()<cr> +ino <silent> <C-l> <c-r>=TriggerSnippet()<cr> +snor <silent> <C-l> <esc>i<right><c-r>=TriggerSnippet()<cr> +ino <silent> <C-\><C-l> <c-r>=BackwardsSnippet()<cr> +snor <silent> <C-\><C-l> <esc>i<right><c-r>=BackwardsSnippet()<cr> +ino <silent> <c-r><C-l> <c-r>=ShowAvailableSnips()<cr> " The default mappings for these are annoying & sometimes break snipMate. " You can change them back if you want, I've put them here for convenience. diff -r 6be3f9c76b17 -r f6e6c3ef4130 autoload/snipMate.vim --- a/autoload/snipMate.vim Tue Jul 05 15:44:20 2011 +0900 +++ b/autoload/snipMate.vim Tue Jul 05 15:46:08 2011 +0900 @@ -198,7 +198,7 @@ if s:curPos == s:snipLen let sMode = s:endCol == g:snipPos[s:curPos-1][1]+g:snipPos[s:curPos-1][2] call s:RemoveSnippet() - return sMode ? "\<tab>" : TriggerSnippet() + return sMode ? "\<C-l>" : TriggerSnippet() endif call cursor(g:snipPos[s:curPos][0], g:snipPos[s:curPos][1]) diff -r 6be3f9c76b17 -r f6e6c3ef4130 plugin/snipMate.vim --- a/plugin/snipMate.vim Tue Jul 05 15:44:20 2011 +0900 +++ b/plugin/snipMate.vim Tue Jul 05 15:46:08 2011 +0900 @@ -147,20 +147,9 @@ endf fun! TriggerSnippet() - if exists('g:SuperTabMappingForward') - if g:SuperTabMappingForward == "<tab>" - let SuperTabKey = "\<c-n>" - elseif g:SuperTabMappingBackward == "<tab>" - let SuperTabKey = "\<c-p>" - endif - endif - if pumvisible() " Update snippet if completion is used, or deal with supertab - if exists('SuperTabKey') - call feedkeys(SuperTabKey) | return '' - endif call feedkeys("\<esc>a", 'n') " Close completion menu - call feedkeys("\<tab>") | return '' + call feedkeys("\<C-l>") | return '' endif if exists('g:snipPos') | return snipMate#jumpTabStop(0) | endif @@ -177,28 +166,14 @@ endif endfor - if exists('SuperTabKey') - call feedkeys(SuperTabKey) - return '' - endif - return "\<tab>" + call ShowAvailableSnips() + return "\<C-p>\<Down>" + "return "\<C-l>" endf fun! BackwardsSnippet() if exists('g:snipPos') | return snipMate#jumpTabStop(1) | endif - - if exists('g:SuperTabMappingForward') - if g:SuperTabMappingBackward == "<s-tab>" - let SuperTabKey = "\<c-p>" - elseif g:SuperTabMappingForward == "<s-tab>" - let SuperTabKey = "\<c-n>" - endif - endif - if exists('SuperTabKey') - call feedkeys(SuperTabKey) - return '' - endif - return "\<s-tab>" + return "\<C-\>\<C-l>" endf " Check if word under cursor is snippet trigger; if it isn't, try checking if
trigger の補完一覧のイメージです:
それでは、よい Vim life を。 Visual Studio で開発している Sleipnir 3 と Eclipse で開発している Sleipnir Mobile for Android (もうすぐα版公開!) もよろしくお願いいたします。 \(^o^)/