unite-source を作成する流れをまとめてみた
この記事は Vim Advent Calendar 2012 171日目の記事になります。
さて、前回の Vim Advent Calendar で iTunes のプレイリストを操作する euphoric_player.vim を公開したんですがその時に作成した unite-source の書き方なんかをまとめてみたいと思います。
[欲しい機能]
- トラック一覧の表示
- 選択したトラックの再生
- 並び順の変更
- 表示形式の変更
今回は euphoric_player.vim を利用して上記の機能を実装した unite-source のコードを載せっていってみたいと思います。
[トラック一覧の表示]
unite.vim でトラック一覧の表示を行います。
この機能は unite-source を使用して定義します。
" unite-source の設定を定義する " " 詳しい設定オプションは " :help unite-source-attributes let s:source = { \ "name" : "tracks", \ "description" : "example unite-source", \} " unite.vim で表示される候補を返す function! s:source.gather_candidates(args, context) " euphoric_player#playlist() の戻り値については " :help euphoric_player#playlist() let playlist = euphoric_player#playlist() if !has_key(playlist, "tracks") return [] endif " トラック名だけ表示 " word に設定した文字列が表示される return map(copy(playlist.tracks), '{ \ "word" : v:val.name, \ }') endfunction " untie.vim に source を登録 call unite#define_source(s:source) unlet s:source
[unite-source で引数を受け取る]
unite.vim では、
:Unite hoge:arg1:arg2
のようにして unite-source に引数を渡すことが出来ます。
これで unite-tracks にプレイリスト名を渡せるようにしてみたいと思います。
let s:source = { \ "name" : "tracks", \ "description" : "example unite-source", \} function! s:source.gather_candidates(args, context) " unite-source の引数は a:args にリストで保存されている " echo a:args " => ['arg1', 'arg2'] " unite-source の引数をそのまま euphoric_player#playlist に渡す let playlist = call("euphoric_player#playlist", a:args) if !has_key(playlist, "tracks") return [] endif return map(copy(playlist.tracks), '{ \ "word" : v:val.name, \ }') endfunction call unite#define_source(s:source) unlet s:source
[選択したトラックを再生する]
unite.vim で選択した候補に対して何かを行う場合、unite-action を定義します。
unite-action の定義方法はいくつかあるんですが、今回は unite-source に追加する形で定義してみたいと思います。
" action を定義する action_table を追加する " :help unite-kind-attribute-action_table " default_action には定義する action を設定 let s:source = { \ "name" : "tracks", \ "description" : "example unite-source", \ "action_table" : { \ "play_track" : { \ "description" : "play", \ } \ }, \ "default_action" : "play_track", \} " action が呼ばれた時の処理を定義 function! s:source.action_table.play_track.func(candidate) " candidate には gather_candidates で設定した値が保持されている call euphoric_player#play_track(a:candidate.action__track_name, a:candidate.action__playlist_name) endfunction function! s:source.gather_candidates(args, context) let playlist = call("euphoric_player#playlist", a:args) if !has_key(playlist, "tracks") return [] endif " action で必要な値を設定する " action で参照される変数名にはプレフィックスとして " action__ " を使用する return map(copy(playlist.tracks), '{ \ "word" : v:val.name, \ "action__track_name" : v:val.name, \ "action__playlist_name" : playlist.name, \ }') endfunction call unite#define_source(s:source) unlet s:source
これで候補を選択した場合に s:source.action_table.play_track.func が呼ばれます。
このように action で必要は値は gather_candidates で定義して返します。
[並び順を変更する]
unite.vim で表示される候補の並び順を変更する場合は unite-filter のsorter を使用します。
この機能は unite-source に対して予め定義しておいた sorter で並び順を設定することが出来ます。
" unite-filter を定義 " この機能で並び順を変更する事が出来る let s:filter = { \ "name" : "sorter_track_name", \} function! s:filter.filter(candidates, context) " v:val.source__track.name で sort する return unite#util#sort_by(a:candidates, 'v:val.source__track.name') endfunction " unite.vim に filter を登録 call unite#define_filter(s:filter) unlet s:filter " 別の sorter も定義してみたり let s:filter = { \ "name" : "sorter_track_artist", \} function! s:filter.filter(candidates, context) " v:val.source__track.artist で sort return unite#util#sort_by(a:candidates, 'v:val.source__track.artist') endfunction call unite#define_filter(s:filter) unlet s:filter let s:source = { \ "name" : "tracks", \ "description" : "example unite-source", \ "action_table" : { \ "play_track" : { \ "description" : "play", \ } \ }, \ "default_action" : "play_track", \} function! s:source.action_table.play_track.func(candidate) call euphoric_player#play_track(a:candidate.action__track_name, a:candidate.action__playlist_name) endfunction function! s:source.gather_candidates(args, context) let playlist = call("euphoric_player#playlist", a:args) if !has_key(playlist, "tracks") return [] endif " unite-filter で参照されるトラック情報も返す return map(copy(playlist.tracks), '{ \ "word" : v:val.name, \ "action__track_name" : v:val.name, \ "action__playlist_name" : playlist.name, \ "source__track" : v:val, \ }') endfunction call unite#define_source(s:source) unlet s:source " unite-tracks でどの sorter を使用するのかを設定する call unite#custom_source('tracks', 'sorters', 'sorter_track_name') " 逆順にする場合 " call unite#custom_source('tracks', 'sorters', ['sorter_track_name', 'sorter_reverse'])
:Unite tracks
こんな感じで unite-source に対してどのように sort を行うのかを設定する事が出来ます。
この機能を利用すれば unite-file などといった既存の unite-source も、ユーザ側で自由に sort を行うことが出来ます。
[表示形式を変更する]
さて、今は曲名しか表示していないのでちょっと寂しいですね。
表示するテキストは unite-source の "word" を変更すればいいのですが、今回は unite-filter の converter を使って表示されるテキストを変更してみたいと思います。
この機能を使用すれば unite-source 側のコードを変更することなく、表示されるテキストを変更する事が出来ます。
" unite-filter を定義 let s:filter = { \ "name" : "converter_my_track", \} function! s:filter.filter(candidates, context) " 候補に対して操作する " 今回は表示されるテキストを変更したいので word に対して設定する let format = "%-30S - %-12S - %S" for candidate in a:candidates let track = candidate.source__track let candidate.word = printf(format, track.name, track.artist, track.album) endfor return a:candidates endfunction " unite.vim に filter を登録 call unite#define_filter(s:filter) unlet s:filter let s:source = { \ "name" : "tracks", \ "description" : "example unite-source", \ "action_table" : { \ "play_track" : { \ "description" : "play", \ } \ }, \ "default_action" : "play_track", \} function! s:source.action_table.play_track.func(candidate) call euphoric_player#play_track(a:candidate.action__track_name, a:candidate.action__playlist_name) endfunction function! s:source.gather_candidates(args, context) let playlist = call("euphoric_player#playlist", a:args) if !has_key(playlist, "tracks") return [] endif " unite-filter で参照されるトラック情報も返す return map(copy(playlist.tracks), '{ \ "word" : v:val.name, \ "action__track_name" : v:val.name, \ "action__playlist_name" : playlist.name, \ "source__track" : v:val, \ }') endfunction call unite#define_source(s:source) unlet s:source " unite-tracks でどの converter を使用するのかを設定する call unite#custom_source('tracks', 'converters', 'converter_my_track')