Create a gist now

Instantly share code, notes, and snippets.

@ujihisa /vac242.md
Last active Dec 20, 2015

What would you like to do?
Vim Advent Calendar 2012 http://atnd.org/events/33746

Vim Advent Calendar 2012 ujihisa 17 (242日目)

Vim Advent Calendar 2012 の242日目の記事です。昨日の記事はmanga_osyoさんのユナイトビューティフルアタックではなくエアラインビューティフルアタックで、明日の記事はkumack55さんの予定です。

先日civilization 5を購入してしまいました。当面時間がなくなる予定です。civilization 5もってるVim使いの人はlingrなどで声をかけてください。ぜひ対戦しましょう!

civilizationという語を並び替えてノイズを除去するとvimになります。

Vim script初心者向け講座その2 〜 unite-locateの実装 〜

前々回、Vim script初心者用入門記事という記事を書きました。これで初心者の方はVim scriptの基本を習得したと思います。そろそろ実践的な話題に入ってもよさそうでしょう。

前回の記事、unite-locate、ここで紹介したプラギンについて実装を紹介しつつ、実践的なVimプラギンの作り方について解説していくことにしましょう。

unite-locateの構成

$ tree -n
.
├── autoload
│   └── unite
│       └── sources
│           └── locate.vim
├── doc
│   ├── tags
│   └── unite-locate.txt
├── Rakefile
└── unite-locate.vimup

4 directories, 5 files

実質autoload/unite/sources/locate.vimdoc/unite-locate.txtです。

vimupは@kana1さんが作成した便利ツールです。Vimプラギン作成者がレシピファイルを作成することで、それに応じてそのプラギンをvim.orgにアップロードするなどの定形業務を行なってくれます。いちいちブラウザでvim.orgにログインしてファイルのアップロードや説明の書き換えを行う必要がなくなるため、すべてのVimプラギン開発者が導入するべき必須ツールといえましょう。

Rakefileはたんにvimupのためのzipファイルを作るためのものです。当時ノリで作ったものの、最近は単にzipコマンドでなんとかしちゃってることが多い気がします・・・。

autoload/unite/sources/locate.vim

というわけで本体です。

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment