Vim で自前バッファを用意するプラグインを書く
この記事は Vim Advent Calendar 2019 23 日目の記事です
最近メールを vim で読むためのプラグインを書いています(道半ば)
機能としては
- メールフォルダの一覧
- フォルダの中のメール一覧
- メール本文の表示
があるのですが、これらを vim 上で表示するためには自分でバッファを用意してやる必要があります
Vim は通常テキストエディタとして使用され、バッファに入っているテキストは何かのファイルに紐付いていることが多いと思います
しかし、今回のメールフォルダ一覧表示のためのバッファなどは特定のファイルに紐付いていません
今回はそのようなバッファをどうすれば用意できるのかについて解説します
なお、各コマンド/関数の詳細や厳密な挙動については触れていると分量が大変なことになると思うので、適宜 help へ解説を投げます
特定のコマンドを実行したらバッファを開く
ディレクトリ構成
testplugin/ - plugin/ - testplugin.vim
スクリプト
testplugin/plugin/testplugin.vim
if exists('g:loaded_testplugin') finish endif let g:loaded_testplugin = 1 let s:save_cpo = &cpo set cpo&vim command! TestPlugin split hoge let &cpo = s:save_cpo unlet s:save_cpo
解説
command!
行以外はおまじないです
二重にプラグインを読まないことや set cpoptions
の上書きなどをしています
詳細は :h use-cpo-save を参照してください
command!
行では TestPlugin
コマンドを定義して、split hoge
を実行するようにしています
つまり、このプラグインを読み込んだ状態で :TestPlugin
を実行すると :split hoge
を実行したのと同様にウィンドウが横に分割され、hoge
という名前のバッファが作成されます
作成したプラグインを読みこむ
ここで作成したプラグインを読みこむ方法はいくつかありますが、今回は雑に .vimrc
に以下を追記して testplugin
ディレクトリ内で vim を起動する、という方法を取ります
.vimrc
" for develop set runtimepath+=$PWD
これで testplugin
ディレクトリにいるときに vim を起動すると :TestPlugin
が実行できるようになっているかと思います
作成したバッファにテキストを追加する
スクリプト
testplugin/plugin/testplugin.vim
を変更していきます
testplugin/plugin/testplugin.vim
if exists('g:loaded_testplugin') finish endif let g:loaded_testplugin = 1 let s:save_cpo = &cpo set cpo&vim command! TestPlugin split hoge | call append(0, 'hoge') let &cpo = s:save_cpo unlet s:save_cpo
解説
:h :bar でコマンドを連結しています
ここでは :h append() で hoge
という文字列をバッファに追加しています
これで :TestPlugin
を実行すると hoge
という名前のバッファに hoge
という文字列が追加されているかと思います
バッファの設定を変更する
ここまでで気付かれている方もおられると思いますが、開いた hoge
という名前のバッファを :q
で閉じようとすると、保存してないぞ、と怒られるかと思います
また :wq
で抜けた場合 testvim
ディレクトリに hoge
という名前のファイルが作成されており、その状態のまま再度 vim を起動し :TestPlugin
を実行すると hoge
が二行表示されるかと思います
これは hoge
というバッファが ./hoge
というファイルに紐付いており、そのファイルが保存/更新されているのでこのような挙動になってしまいます
ここではそれを防ぎ、特定のファイルに紐付かないようにバッファの設定を変更していきます
ついでに処理が長くなってきたので関数に分離していきます
ディレクトリ構成
testplugin/ - plugin/ - testplugin.vim - autoload/ - testplugin.vim
スクリプト
testplugin/plugin/testplugin.vim
if exists('g:loaded_testplugin') finish endif let g:loaded_testplugin = 1 let s:save_cpo = &cpo set cpo&vim command! TestPlugin call testplugin#open_buffer() let &cpo = s:save_cpo unlet s:save_cpo
testplugin/autoload/testplugin.vim
let s:save_cpo = &cpo set cpo&vim function! testplugin#open_buffer() abort split hoge call append(0, 'hoge') call deletebufline('%', '$') setlocal buftype=nofile setlocal bufhidden=hide setlocal noswapfile nobuflisted endfunction let &cpo = s:save_cpo unlet s:save_cpo
(Updated(2019/12/23 16:54): command
行に call
がない)
解説
autoload については :h autoload/:h 41.15 を参照してください
ここでは testplugin/autoload/testplugin.vim
で定義された testplugin#open_buffer()
が他から参照できるという点のみが重要です
:TestPlugin
を実行するとこの testplugin#open_buffer()
が呼ばれます
testplugin#open_buffer()
ではこれまでの処理に加えて deletebufline()
で最後に表示されていた改行の削除とバッファ設定の変更をしています
buftype
をnofile
にすることで:w
ができないようにしますbufhidden
をhide
にして:q
から抜けたときにバッファを隠しバッファとしますnoswapfile
でこのバッファについて swap ファイルを作成しないようにしますnobuflisted
でこのバッファを:ls
などで見えないようにします
詳細については :h special-buffers などを参照してください
// 正直この辺どう設定したらいいかはまだ試行錯誤中なので他の人の知見も知りたいところです
// 例えば nomodifiable
も設定したいけれど、設定すると :q
してからもう一度 :TestPlugin
するとエラーで怒られてしまうのでどうしよう? とか
ここまででファイルに紐付かないバッファを作るプラグインが出来ました
のでこれで終わり、と言いたいところですが、最後に少しだけ変更を加えます
バッファ名を変更する
今は :TestPlugin
により開かれるバッファ名は hoge
になっていますが、:TestPlugin
を実行して :q
してから :e hoge
を実行すると分かるように、:e hoge
により開かれる hoge
というバッファは新規ファイルではなく、既存の :TestPlugin
により開かれたバッファになります
これでは hoge
という名前のファイルが作成できなくて困ってしまうので、プラグイン独自のバッファ名を付けましょう
testplugin/autoload/testplugin.vim
let s:save_cpo = &cpo set cpo&vim function! testplugin#open_buffer() abort split testplugin://hoge call append(0, 'hoge') call deletebufline('%', '$') setlocal buftype=nofile setlocal bufhidden=hide setlocal noswapfile nobuflisted endfunction let &cpo = s:save_cpo unlet s:save_cpo
testplugin://hoge
という名前に変更しました
被りさえしなければ何でもいいのですが、:h Cmd-event を利用してバッファを作成しているプラグインなど プラグイン名://バッファ名
のようにしているプラグインがいくつか見られるのでそれに合わせてみました
// Cmd-event
を利用したバッファ作成方法も解説したかったのですが、私自身がまだ理解しきれていないので今回はここまでです...
というわけで、自前でバッファを用意するプラグインを書くことができました