Vim Advent Calendar 2012 の242日目の記事です。昨日の記事はmanga_osyoさんのユナイトビューティフルアタックではなくエアラインビューティフルアタックで、明日の記事はkumack55さんの予定です。
先日civilization 5を購入してしまいました。当面時間がなくなる予定です。civilization 5もってるVim使いの人はlingrなどで声をかけてください。ぜひ対戦しましょう!
civilizationという語を並び替えてノイズを除去するとvimになります。
前々回、Vim script初心者用入門記事という記事を書きました。これで初心者の方はVim scriptの基本を習得したと思います。そろそろ実践的な話題に入ってもよさそうでしょう。
前回の記事、unite-locate、ここで紹介したプラギンについて実装を紹介しつつ、実践的なVimプラギンの作り方について解説していくことにしましょう。
$ tree -n
.
├── autoload
│ └── unite
│ └── sources
│ └── locate.vim
├── doc
│ ├── tags
│ └── unite-locate.txt
├── Rakefile
└── unite-locate.vimup
4 directories, 5 files
実質autoload/unite/sources/locate.vim
とdoc/unite-locate.txt
です。
vimupは@kana1さんが作成した便利ツールです。Vimプラギン作成者がレシピファイルを作成することで、それに応じてそのプラギンをvim.orgにアップロードするなどの定形業務を行なってくれます。いちいちブラウザでvim.orgにログインしてファイルのアップロードや説明の書き換えを行う必要がなくなるため、すべてのVimプラギン開発者が導入するべき必須ツールといえましょう。
Rakefileはたんにvimupのためのzipファイルを作るためのものです。当時ノリで作ったものの、最近は単にzip
コマンドでなんとかしちゃってることが多い気がします・・・。
というわけで本体です。
1 let s:save_cpo = &cpo
2 set cpo&vim
3
4 let s:unite_source = {
5 \ 'name': 'locate',
6 \ 'max_candidates': 30,
7 \ 'is_volatile': 1,
8 \ 'required_pattern_length': 3,
9 \ }
最初の2行はおまじないです。
s:unite_source
はuniteのsourceとして登録するための辞書です。詳しくは:h unite-create-source
を参照してください。
- locateコマンドは大量の結果を返すことがほとんどなため、
max_candidates
を指定して快適なUXに - uniteのnarrowing windowで実際の検索を行わせたいため、
is_volatile
を設定。詳しくは:h unite-source-is_volatile
を参照のこと。 - locateコマンドを短い文字数の引数で実行するとこれまた結果が大きすぎるため実用的でないので、
max_candidates
とは独立して、さらにrequired_pattern_length
も指定
次の部分はちょっぴり難易度があがります。
11 " If the locate command is linux version, use -e option which means fetching
12 " only existing files.
13 function! s:is_linux()
14 " Linux version only has -V option
15 call unite#util#system('locate -V')
16 return !unite#util#get_last_status()
17 endfunction
18
19 if exists('g:unite_locate_command')
20 let s:locate_command = g:unite_locate_command
21 elseif executable('locate')
22 let s:locate_command = 'locate -l %d'.(s:is_linux() ? ' -e' : '').' %s'
23 elseif unite#util#is_windows() && executable('es')
24 let s:locate_command = 'es -i -r -n %d %s'
25 endif
まずis_linux()
というスクリプトローカルな関数を定義しています。
コメントにあるように、locateコマンドの挙動がbsd系とlinux系 (gnu系かな?) で違うため、それをはん亭するための関数を作っています。ちなみにこれ関数名はis_linux()
じゃないほうがいい気がしてきました。気になる方はぜひpull requestに挑戦してみてはいかがでしょうか。
そして後半部分でs:locate_command
を環境に応じて定義しています。これはあとで利用します。
Windows環境ではlocate
のかわりにes
という謎のコマンドを使うようにしているところなどを見つけた方はさすがです。そうです私はes
を利用します。
27 function! s:unite_source.gather_candidates(args, context)
28 return map(
29 \ split(
30 \ unite#util#system(printf(
31 \ s:locate_command,
32 \ s:unite_source.max_candidates,
33 \ a:context.input)),
34 \ "\n"),
35 \ '{
36 \ "word": v:val,
37 \ "source": "locate",
38 \ "kind": "file",
39 \ "action__path": v:val,
40 \ "action__directory": fnamemodify(v:val, ":p:h"),
41 \ }')
42 endfunction
この部分はとても素直ですね。単に外部コマンドlocate
ぽいものをs:locate_command
で参照してunite#util#system()
で実行し、その結果をいい感じにしているだけです。
unite#util#system()
はようするにVitalのPrelude.system()
です。uniteが独自にラップして提供しているため、uniteプラギンは個別にvitalizeすることなくunite経由でvitalの関数が利用でき、大変便利です。
44 function! unite#sources#locate#define()
45 return exists('s:locate_command') ? s:unite_source : []
46 endfunction
ここまでがんばって作ってきたs:unite_source
をuniteに登録するための定型句です。
49 let &cpo = s:save_cpo
50 unlet s:save_cpo
おまじないです。
というわけでついにuniteプラギンが完成しました! 感極まったらこれをgithubとvim.orgにリリースします。
$ vimup unite-locate