Vimプラグインの拡張機能プラグインを作ってVimをさらに使いやすくしよう
この記事はVim Advent Calendar 2012 の 31 日目の記事です。
2012年も今日で終わりですね。12月は忙しかったので気付いたら年末でした。
最近はXcodeばかりを使用していたのでVimエディタからは離れていましたが、XcodeをVimライクにするプラグインのXVimを使ってました。XVim最高ですね!
@jugglershuさんの27日目の記事で知ったのですが、ViciousというXcodeのプラグインもあるらしいです。
でも、私はXVimを使うでしょう。なぜなら、ソースコードが公開されているから。
今度、時間があれば、気になったところを修正してpull requestしたいものです。
さてさて本題です。皆さんはVimを使ってるときに欠かせないプラグインがそれぞれあると思います。
私の場合だとctrlp.vimがそれにあたるプラグインです。
このようなプラグインは他に unite.vim がありますが、github.com を見る限り ctrlp.vim は相当使われているプラグインです。
今回、その ctrlp.vim の機能を拡張させる拡張プラグインの作り方を説明します。
ctrlp.vim の拡張プラグインの作り方
本家の extensions ブランチに sample.vim があります。
以下の説明は大体、このサンプルを基に作っております。
また、今回、Vim advent calendarのために軽いものを作ってみました。
gitのログをCtrlPの結果に出して、ログを選択するとそのコミットの詳細を見れる簡単な拡張機能です。
中身はこんな感じになってます。
ctrlp-git-log ├── README.mkd ├── autoload │ └── ctrlp │ └── git_log.vim └── plugin └── ctrlp-git-log.vim
autoloadフォルダにあるファイル (git_log.vim) でCtrlPの拡張機能を実現しています。
pluginフォルダではautoloadを呼び出すコマンドを定義しているだけです。
では、autoloadのファイル (git_log.vim) を説明していきます
autoload/ctrlp/git_log.vim
拡張機能の設定
拡張機能の設定を記述します。
作ったプラグインだと s:git_log_var です。これを g:ctrlp_ext_vars に加えます。
let s:git_log_var = { \ 'init': 'ctrlp#git_log#init()', \ 'accept': 'ctrlp#git_log#accept', \ 'lname': 'git-log', \ 'sname': 'git-log', \ 'type': 'line', \ 'enter': 'ctrlp#git_log#enter()', \ 'exit': 'ctrlp#git_log#exit()', \ 'sort': 0, \ } if exists('g:ctrlp_ext_vars') && !empty(g:ctrlp_ext_vars) let g:ctrlp_ext_vars = add(g:ctrlp_ext_vars, s:git_log_var) else let g:ctrlp_ext_vars = [s:git_log_var] endif
この設定では必ず必要なものと、オプションで必要ならば記述するものがあります。
それが下記となります。
Required
- init: ctrlp#git_log#init()
- accept: ctrlp#git_log#accept
- lname: long statusline name
- sname: short name
- type: matching type
Optional
- enter: ctrlp#git_log#enter()
- exit: ctrlp#git_log#exit()
- opts: ctrlp#git_log#opts()
- sort: 0
init, accept, enter, exit そして opts には関数を定義します。
残りは、それぞれ値を代入します。
Required
init: ctrlp#git_log#init()
initには関数を定義し、その中にCtrlPで表示して欲しいリストをreturnします。
今回作ったプラグインでは、gitのコミットログ1つずつをctlrpの1行に表示させるためにgit-logにいろいろとオプションをつけています。
function! ctrlp#git_log#init() let s:log = split(system('git log --oneline -50 --pretty=format:"%h | %cr, %an - %s"'), "\n") return s:log endfunc
1行に整形させたコミットログを改行で分割したリストにしてそれをreturnしています。
ちなみに、git-logオプションの説明
- git log --oneline -50 --pretty=format:"%h | %cr, %an - %s"
- --oneline => 1行で出力(デフォルトだとコミットハッシュとコミットメッセージのみ)
- -50 => ログは最新から遡って最高で50コミットだけ取得
- --pretty=format:"%h | %cr, %an - %s" => ハッシュ | 現在からの相対時間, コミットした人 - コミットメッセージ
accept: ctrlp#git_log#accept
initによりCtrlPで表示させたコミットログのリストを選択したときに起動します。
function! ctrlp#git_log#accept(mode, str) call ctrlp#exit() let hash = substitute(a:str, "^\\(.\\+\\)\\s|.*$", "\\1", "") echo system('git log '.hash.'~..'.hash.' --stat -p') endfunction
modeには選択するときに押されたキーによってそれぞれ値が代入されます。
そのmodeによって処理分けをしたりしますが、大抵のプラグインでは何もしていないと思います。
mruやfile を開くデフォルトのものだと
- mode
- <cr> => e: バッファ
- <c-v> => v: 縦分割のウィンドウ
- <c-t> => t: 新しいタブ
- <c-x> => h: 横分割のウィンドウ
strには、選択された文字列がそのまま入ります。
git_log.vim では選択したgitログのコミットハッシュを置換によって整形し、取得しています。
そのハッシュを使って git log を該当のコミットの詳細を出力しています。
ちなみに、acceptの最初にある
call call#exit()
で、ctrlpを終了してからそれぞれの処理をしています。
lname & sname
lnameはステータスラインに表示されるロングネーム、snameはショートネームのことです。 今回はそのまま、 git-log にしました。
type: matching type
typeは、ctrlp実行時のリストに入力した文字列をどの範囲でマッチングさせるかを指定します。
- line: 1行全てに対してマッチング
- path: ファイルやディレクトリパスのような1行全てに対してマッチング
- tabs: 最初のタブ文字までのマッチング
- tabe: 最後のタブ文字までのマッチング
よくわからなければ line を選んでしまって問題ないです。
type に tabs を設定して、CtrlP で下記のように1行の中身がタブで区切られているリストが出力されているとき、bar を入力すると 'bar baz foo' のみマッチングします。
foo bar baz bar baz foo baz foo bar
Optional
enter: ctrlp#git_log#enter()
initより先に呼ばれます。
たまに、initの中では取得できない値なんかをctrlp#foo#id()の中で定義している拡張プラグインがありますが、そういったときにctrlp#foo#enter()内で定義します。
編集中のファイルタイプなんかは、init中では取得できません。
今回のプラグインではこのように記述してもOKです。
function! ctrlp#git_log#init() return s:log endfunc function! ctrlp#git_log#enter() let s:log = split(system('git log --oneline -50 --pretty=format:"%h | %cr, %an - %s"'), "\n") endfunc
exit: ctrlp#git_log#exit()
CtlPを終える前にしたい処理を書くのですが、今まで使ったことがないです。
なにか後処理をしたいときに使用してください。
opts: ctrlp#git_log#opts()
初期化されるときに呼ばれる関数で、オプションを設定するなどをすべきようです。
これも使ったことがない…
sort: 0
sort を設定しない場合、CtrlPには結果がソートされたものが出力されます。
git_log.vimではgitのログについてはソートされていない方がコミットを追えるのでソートをオフにしています。
ctrlp#git_log#id()
CtrlPのおまじない
let s:id = g:ctrlp_builtins + len(g:ctrlp_ext_vars) function! ctrlp#git_log#id() return s:id endfunction
これで、拡張プラグインが作成できます。
コマンドは.vimrcに定義してもいいのですが、せっかくなのでpluginのフォルダに ctrlp-git-log.vim のようなファイルを作り
command! CtrlPGitLog cal ctrlp#init(ctrlp#git_log#id())
のように定義します。
これで、:CtrlPGitLog とコマンドを打てば使用できますし、.vimrc に
let g:ctrlp_extensions = [ \'git_log' \]
を追加してあげると、<c-p> で起動したときに <c-f> or <c-b> で表示されるようになります。
実際に起動してみるとこのように出力されます
どれかを選択すると
と git log --stat -p の結果が見れます。
特定のコミットハッシュのログに限定するために
git log hash~..hash
のように指定しているので、Initial commitを選択するとエラーが出ます。
特定のコミットハッシュのログを見る違う方法って他にないんですかね?
終わりです
これで、CtrlPの拡張プラグインの書き方がわかると思います。
これからVim pluginを書いてみたい!と、思っている人。
プラグインの拡張プラグインならすごく簡単に書けるのでオススメです。
というより、CtrlPはオススメですのでぜひ使って下さい。
使っているうちに、あの結果を手軽くやりたいと思うので、その時にこの記事を再度眺めてみて下さい。
Let's create Vim plugins or extensions!!
では、今年も24時間切っておりますので、来年もよろしくお願いします!
次回のVim Advent Calendar
2013年最初のVim Advent Calendar 2012 は@tavi4444さんです。