reanimate.vim というプラグインをつくった

さて、今日はチョコの日ということで unite-valentine でもつくろうかと思ったんですが、アニメーションの実装がしんどかったので断念しました。
その代わりといってはなんですが、以前からつくっていた Vim の復元プラグインでもネタにしたいと思います。
(と、いうかこういう機会がないととズルズルと引っ張りそうなので…。
そんな感じで簡単な使い方とかを書いてみます。
まだ、つくっている途中なので破壊的な変更があったらごめんなさい。
unite-session なにそれおいしいの

[概要]

現在の Vim の状況を名前を付けて保存し、復元を行うプラグインです。

[テスト環境]

この環境以外ではテストしていないので動かなかったらごめんなさい。

[プラグイン]

Neobundle "git://github.com/osyo-manga/vim-reanimate.git"

[簡単な使い方]

" MySaveData という名前を付けて保存
:ReanimateSave MySaveData

" 保存した MySaveData を復元
:ReanimateLoad MySaveData

このように名前をつけて保存し、名前を指定して復元を行います。

[保存/復元するデータ]

  • session
  • viminfo
  • ウィンドウの位置とサイズ

保存/復元するデータですが session や viminfo は Vim に依存しているので、特にプラグイン側で特別な事を行なっているわけではありません。
別のいい方をすれば、session や viminfo で出来ないことは基本的に出来ない、と思ってもらった方がいいです。

[コマンド]

コマンド 動作
ReanimateSave {name} {name} で保存。省略されれば最後に保存/復元した名前か g:reanimate_default_save_name が使用される
ReanimateSaveInput 名前を入力して保存
ReanimateLoad {name} {name} で保存。省略されれば最後に保存/復元した名前か g:reanimate_default_save_name が使用される
ReanimateLoadInput 名前を入力して復元
ReanimateSaveCursorHold autocmd CursorHold で呼び出す場合に使用するコマンド。基本的には ReanimateSave と等価

[オプション]

オプション名 デフォルト値 説明
g:reanimate_save_dir "~/reanimate/save_dir" 保存するディレクト
g:reanimate_default_save_name "latest" デフォルトの保存名
g:reanimate_sessionoptions &sessionoptions sessionoptions
g:reanimate_disables [] 保存・復元を無効にする機能

g:reanimate_disables には

"reanimate_session", "reanimate_viminfo", "reanimate_window"

の3つが設定できます。

[example]

" 保存先のディレクトリ
let g:reanimate_save_dir = $VIM."/.vim/save_point"

" :ReanimateSave<CR>
" のように引数がない場合に使用される名前です
let g:reanimate_default_save_name = "latest"

" sessionoptions
let g:reanimate_sessionoptions="curdir,folds,help,localoptions,slash,tabpages,winsize"

" session と viminfo の処理をしない
" reanimate_session
" reanimate_viminfo
" reanimate_window
" の3つのいずれかをリストで設定します
let g:reanimate_disables = ["reanimate_session", "reanimate_viminfo"]

[unite-reanimate]

unite.vim を使用して、保存されているデータを列挙して保存/復元を行うことが出来ます。
また、デフォルトでは、default-action には何も設定していないので、action から reanimate_save または、reanimate_load を選択するか、起動時の -default-action 引数に渡して使用します。
わたしは下記のようなコマンド呼び出しをマッピングして、保存/復元の選択を行っています。

" :Unite reanimate の呼び出し時に default-action を設定

" 保存
:Unite reanimate -default-action=reanimate_load
" 復元
:Unite reanimate -default-action=reanimate_save

実際の出力画面はこんな感じになります。

[使い方]

わたしが使っている使い方とか。

  • 作業ごとにデータを保存しておく(cpp や vimvim script 等)
  • Vim の起動直後に作業したいデータを復元する
  • 別の作業をする場合はそのデータを復元する(以下ループ。

大まかにはこんな感じですね。
C++Vim script なんかの作業状況をサッと切り替えたり、プロジェクトごとに保存とかしています。


また、わたしは下記のような設定を行っています。

" 保存先のディレクトリ
let g:reanimate_save_dir = $VIM."/.vim/save_point"

" デフォルトの保存名
let g:reanimate_default_save_name = "latest"

" sessionoptions
let g:reanimate_sessionoptions="curdir,folds,globals,help,localoptions,slash,tabpages,winsize"

" 無効にする機能があれば
" let g:reanimate_disables = ["reanimate_session", "reanimate_viminfo", "reanimate_window"]


" ステータスラインに現在の保存名を出力
function! Last_point()
    return reanimate#is_saved() ? reanimate#last_point() : "no save"
endfunction
set statusline=%=[%{Last_point()}\]\[%{(&fenc!=''?&fenc:&enc)}/%{&ff}]\[%03l,%03v]

" オートコマンド
augroup SavePoint
    autocmd!
    " 終了時に保存を行う
    autocmd VimLeavePre * ReanimateSave

    " バッファに書き込む時に一緒の保存する
"    autocmd BufWritePost * ReanimateSave
    
    " CursorHold 時には ReanimateSaveCursorHold を使用する
"     autocmd CursorHold * ReanimateSaveCursorHold
    
    " 自動的に復元する場合
"     autocmd VimEnter * ReanimateLoad
augroup END


" ユーザ側で reanimate.vim のイベントに処理を hook する
let s:event = {
\    "name" : "user_event",
\}

function! s:event.load_pre(...)
    " 読み込み前に全てのバッファを保存
    :wall
    " 復元前にタブを削除する
    :tabonly
endfunction

function! s:event.save_pre(...)
    " 保存前に args を削除する
    try
        :execute "argd *"
    catch
    endtry
endfunction

call reanimate#hook(s:event)
unlet s:event

autocmd 等は上記を参考にして設定してみて下さい。
上記のようにユーザ側で reanimate.vim のイベントに処理を hook する事が出来るのですが、説明すると量が多くなるので今回は割愛します。

[vimrc_local.vim]

各データが保存されているディレクトリ(例えば g:reanimate_save_dir."/latest")に vimrc_local.vim というファイルを保存しておくことでデータの復元時に vimrc_local.vim の :source を行います。
例えば『このデータを復元する場合はこういう Vim の設定にしたい』という場合に便利です。
vimrc_local.vim についての詳細はこことかを参照して下さい。

[Q&A]

以下、自問自答。

  • Q.新規で名前をつけて保存したい
    • :ReanimateSaveInput もしくは :ReanimateSave {名前} で名前を付けて保存することが出来ます。
  • Q.特定の保存/復元する機能を無効にしたい
    • g:reanimate_disables に無効にする機能をリストで記述します。
  • Q.unite-reanimate で選択しても何も起きない
    • default-action が設定されていないので何も起きません。
    • unite.vim の起動時に -default-action=reanimate_load のように設定するか、unite.vim のアクション一覧から選んで下さい。
  • Q.復元時にカレントのタブ以外が残っている
    • Vim の session はカレントのタブに復元を行うので、カレントのタブ以外はそのまま残っています。
    • 下記のような Vim script を記述することで復元前にかれんと以外のタブを削除することが出来ます。
let s:event = {
\    "name" : "user_event",
\}

" 復元前のイベント処理を記述
function! s:event.load_pre(...)
    " 復元前にタブを削除する
    :tabonly
endfunction

call reanimate#hook(s:event)
unlet s:event
  • Q.ファイルが存在しないバッファを復元して欲しくない
    • sessionoptions に buffers が設定されていると隠れバッファも保存されてしまいます。
    • そういうバッファを復元してほしくない場合は、g:reanimate_sessionoptions または、sessionoptions から buffers の設定を取り除いて下さい。
" 例えばこんな設定
let g:reanimate_sessionoptions="curdir,folds,globals,help,localoptions,slash,tabpages,winsize"
  • Q.ステータスラインに現在の保存名を出力したい
    • 下記のような設定を行うことで出力することが出来ます。
" ステータスラインに現在の保存名を出力
function! Last_point()
    return reanimate#is_saved() ? reanimate#last_point() : "no save"
endfunction
set statusline=%=[%{Last_point()}\]\[%{(&fenc!=''?&fenc:&enc)}/%{&ff}]\[%03l,%03v]
  • Q.autocmd CursorHold 時にデータを保存したい
    • autocmd CursorHold 時に :ReanimateSave を使用すると不都合が出てくるので、ReanimateSaveCursorHold を使用する必要があります。
" CursorHold 時には ReanimateSaveCursorHold を使用する
autocmd CursorHold * ReanimateSaveCursorHold
  • Q.データを削除したい
    • 現状は Vim から削除する方法はありません。
    • g:reanimate_save_dir に保存されているデータディレクトリを直接削除して下さい。
  • Q.名前を変更したい
    • 現状は Vim から名前を変更する方法はありません。
    • g:reanimate_save_dir に保存されているデータディレクトリ名を直接変更して下さい。
  • Q.vimfiler や vimshell などのファイルが存在しないバッファも復元したい
    • 現状は無理です。
  • Q.vimrc_local.vim を使いたい
    • 各保存ディレクトリの直下に vimrc_local.vim を作成してそこに記述して下さい。
  • Q.終了時に自動的に保存して欲しい
    • プラグイン側で autocmd の設定は行っていません。
    • 下記のようにユーザ側で autocmd を設定する必要があります。
augroup SavePoint
    autocmd!
    " 終了時に保存を行う
    autocmd VimLeavePre * ReanimateSave

    " バッファに書き込む時に一緒の保存する
"     autocmd BufWritePost * ReanimateSave
    
    " CursorHold 時には ReanimateSaveCursorHold を使用する
"     autocmd CursorHold * ReanimateSaveCursorHold
    
    " 自動的に復元する場合
"     autocmd VimEnter * ReanimateLoad
augroup END

[今後実装するかも知れない機能]

  • 保存データの削除
  • 保存名の変更
  • vimfiler や vimshell の復元
  • データの履歴を保存/復元
  • 一箇所に保存するのではなくて、個別のディレクトリを設定できるようにする
  • 読み込みに失敗したときにでも強制的に復元する方法
  • vimrc_local の作成等
  • unite-reanimate の並び順を任意の値でソート
  • 量が多くなってきた場合に備えて保存ディレクトリを階層構造にする

※あくまでも予定


と、こんな感じでざっくりと書いてみました。
つくった本人がいうのは当たり前ですが自分では、なかなかいい感じのプラグインに仕上がったと思っています。
特に vimrc_local.vim を活用することによって擬似プロジェクト管理を行うことも出来ます。
まぁ hook などの細かい部分はおいおいブログにでも書いていこうと思います。
うーむ、説明難しいですね。
やはりキャプチャした動画(と画像)が欲しい。
どうでもいいけど、Q&Aとかよくある質問的なものは他のプラグインの help とかにあってもいいんじゃないかと思わなくもない。





……あ、ちなみにわたしは今年2個のチョコを頂きましたよ!(うち1つはゲーマーズでもらってもう1つは…。