vimでひかえめな自動保存

Vim Advent Calendar 2013、 67日目の記事になります。

御存知の通り、 webapi-vimの出現により vimによって簡単にwebサービスを読み書きができるようになりました。 さらにunite.vimvim-metarwを 使用する事で簡単にインターフェイスを構築できます。

しかし、これらのサービスのAPIの応答は、 一般にローカルに保存する場合に比べ圧倒的に時間がかかります。 さらに現状のvimではajaxのようにスクリプトを非同期実行できない。 もしvim-metarwのwriteで毎回送信する場合、 :wと押すたびに数秒待たされるかもしれない。 かといって手動で送信するのは忘れがち。

つまりアクセスの回数を絞った自動保存機能が欲しい

試行錯誤編

という事で試行錯誤が始まります。 多分autocmdにそれっぽいトリガーがあるだろうと思ってhelpを読む事数十分。。。 (あまり読みなれてない)

BufUnload       Before unloading a buffer.  This is when the
                text in the buffer is going to be freed.  This
                may be after a BufWritePost and before a
                BufDelete.  Also used for all buffers that are
                loaded when Vim is going to exit.
                NOTE: When this autocommand is executed, the
                current buffer "%" may be different from the
                buffer being unloaded "<afile>".
                Don't change to another buffer, it will cause
                problems.
                When exiting and v:dying is 2 or more this
                event is not triggered.

多分これっぽいんだけど、なんか後半に良くわからない事が書いてるorz

めげずに<afile>について調べると、

<afile>  When executing autocommands, is replaced with the file name
         for a file read or write.
<abuf>   When executing autocommands, is replaced with the currently
         effective buffer number (for ":r file" and ":so file" it is
         the current buffer, the file being read/sourced is not in a
         buffer).

つまりバッファがアンロードされる時の現在のバッファ(%)が トリガーが指定されたバッファ(<abuf>)と違うかもしれないという事か!!(オウム返し) :bdeleteで消す場合とか:qaで全部閉じる場合を考えろってことか。

ともかくこれで行ける!? <abuf>はきっとexpand()使うんだろう:

function! s:buffer_infos()
    let file_name = expand("<afile>")
    let buffer_number = expand("<abuf>")
    let buffer_name = bufname(buffer_number)
    echo "File name    : " . file_name
    echo "Buffer number: " . buffer_number
    echo "Buffer name  : " . buffer_name
    let lines = getbufline(buffer_number, 1, '$')
    echo lines
endfunction

augroup test_trigger
    autocmd!
    autocmd BufUnload test.vim call s:save_buffer_to_file()
augroup END

多分これでいけるはず!! :so %してバッファを閉じるぜ!!!

File name    : test.vim
Buffer number: 1
Buffer name  : 
[]

(´・ω・`)ショボーン

bufname({expr})                        *bufname()*
        ...
        If the {expr} is a String, but you want to use it as a buffer
        number, force it to be a Number by adding zero to it: >
            :echo bufname("3" + 0)
        If the buffer doesn't exist, or doesn't have a name, an empty
        string is returned. 

これか?

function! s:buffer_infos()
    let file_name = expand("<afile>")
    let buffer_number = expand("<abuf>")
    let buffer_name = bufname(buffer_number + 0)
    echo "File name    : " . file_name
    echo "Buffer number: " . buffer_number
    echo "Buffer name  : " . buffer_name
    let lines = getbufline(buffer_number, 1, '$')
    echo lines
endfunction
File name    : test.vim
Buffer number: 1
Buffer name  : test.vim
[ "function! s:buffer_infos()", "    let file_name = expand("<afile>")", "    let buffer_number = expand("<abuf>")", "    let buffer_name = bufname(buffer_number)", "    echo "File name    : " . file_name", "    echo "Buffer number: " . buffer_number", "    echo "Buffer name  : " . buffer_name", "    let lines = getbufline(buffer_number, 1, '$')", "    echo lines", "endfunction", "", "augroup test_trigger", "    autocmd!", "    autocmd BufUnload test.vim call s:save_buffer_to_file()", "augroup END", ]

大勝利!!! 別バッファから:bdeleteしても上手く動く (`・ω・´)キリッ

まとめ

最終的に

function! s:send(lines)
    " send data to web service
endfunction

function! s:wrapper()
    let buffer_number = expand("<abuf>")
    let lines = getbufline(buffer_number + 0, 1, '$')
    call s:send(lines)
endfunction

augroup ModestAutoSave
    autocmd!
    autocmd BufUnload * call s:wrapper()
augroup END

とすればいい!パターンの部分は適当に。

おわりに

プラグインとして公開しようと思っていたのですが、 autocmdのパターンの扱い(変数が使えない?)がよくわからなかったので無期限延期しました。 結局すごく短かいですし。

最近vimプラグインを作成し始めた新参なので、 間違い等指摘してもらえると助かります。