Vimスクリプトの初心者がコマンドを定義してみた

仕事でとあるログを調査する機会が多いのですが(なんのログかはナイショだよ!)、grepで見やすくフィルタリングするための検索ワードをいちいち他のテキストからコピペで持ってくるのが面倒くさい。

そんなわけで、今回はVimスクリプトでコマンドを定義してどうにかしてみようと思います。とはいえVimスクリプトはまったくと言っていいほどの初心者(コピペオンリー)なので、まずはコマンドの定義から、ですかね。

コマンドを定義してみる

まずは決まったテキストから決まった検索ワードでgrepするだけのプロトタイプを作成してみます。grepにはvimgrepを使います。

下記の内容をgrepLog1.vimというファイル名で~/testに保存。

command GrepLog vimgrep /hoge/g ~/test/test.txt | copen

検索対象となるテキストとして~/test/test.txtを用意します。

hoge
fuga
hogehoge
hogefuga

それでは実行してみます。
まずはVimから:sourceで保存したスクリプトを読み込んで、続いて:GrepLogで実行。

:source ~/test/grepLog1.vim
:GrepLog

上手く動きました。(結果は明白なので割愛)

引数でファイルパスを指定できるようにする

コマンドの定義は上手くいったものの、ファイルパスを指定できないのでは実用には程遠いため、次はこの点を改善してみたいと思います。

command -nargs=1 -complete=file GrepLog vimgrep /hoge/g <args> | copen
  • nargs=1で引数を1つ受け取るように設定。-complete=fileで引数にファイルとディレクトリの補完が効くように設定。が引数となります。これらの詳細は:help commandを参照。

これも上手く動きました。ワイルドカードで複数のファイルを指定しても問題ナッシングです。(例えばこんな感じ:GrepLog ./**/*.{log,txt})

引数により動作を変えてみる

先ほどの内容でも検索ワードをコピペしなくて済むため当初の目的は達成できたように思われますが、実は十分ではありません。というのも、私が調査するログは実行するオペレーションによって内容がかなり違ってくるため、検索ワードもそれに合わせて変える必要があるからです。(もちろんどんなオペかはナイショだよ!)

function GrepLogFunc(kind, filepath) abort
    if a:kind == 'alpha'
        let l:pattern = '/hoge/g'
    elseif a:kind == 'beta'
        let l:pattern = '/fuga/g'
    elseif a:kind == 'ganma'
        let l:pattern = '/geso/g'
    else
        let l:pattern = '/hoge/g'
    endif

    execute 'vimgrep' l:pattern a:filepath
    copen
endfunction

command -nargs=+ -complete=file GrepLog call GrepLogFunc(<f-args>)

ついに関数も初体験。行数も増え、少し本格的になってきたような気がします。今回は引数にログの種類(この例では'alpha', 'beta', 'ganma')を指定することにより検索ワードを変更できるようにしています。

特に難しいことはしていないため問題なく動くだろうと思っていたところ、vimgrepを実行するところでかなりハマってしまいました。最初はvimgrep l:pattern a:filepathのようにしていたのですが、引数が展開されない現象が発生。仕方がないのでexecuteを使うようにしたところ上手くいきました。なぜexecuteなしでは上手くいかなかったのか謎ですが、こういう解決方法もあるということで勉強になったので、まあよしとします。

あと、functionにabortを付け、||を使用して実行していたcopenをvimgrepが終わった後に実行するように変更しました。これでvimgrepでファイルが見つからない場合やマッチする検索ワードがない場合、すぐ関数を抜けるためcopenが実行されないようになります。

変数の前のa:やl:、コマンドから関数に引数を渡すときのは、それぞれ:help internal-variablesと:help commandを参照。

とりあえず動くようになったので今回はここまでとします。次回はユーザー定義の補完を使ってコマンド入力を簡単にできるようにする予定です。