指定ディレクトリ下のファイルを更新時に自動アップロードするVimScript
ローカルのPC環境で,サーバーにアップロードすることを前提としたファイル,例えばHTMLファイルやCGIスクリプトなどを編集するときは,表示の確認やスクリプトの動作確認のために,以下のプロセスを繰り返すことになります.
- ファイルを更新
- サーバーにアップロード
- (動作|表示)確認
更新したファイルをサーバーにファイルをアップロードする際には,Linuxではftp,scpやrsyncといったコマンド,WindowsではFFFTP,WinSCPなどのツールを使うことが多いと思います.このとき問題になるのが
たとえコマンド1回で済むとしても,ファイルを更新するたびにアップロードして確認するのが,激しく面倒くさい
ということです.個人的にこの面倒くささを解消したくて,過去に色々と対応策を試しましたが,どれも一長一短だというのが結論です.
リモートに編集環境を構築する場合の問題点
- ファイルの編集に必要なソフトウェアをサーバーにインストールする権限が必要
- レンタルサーバーなどでは難しい場合もある
- サーバーが複数ある場合,編集環境がバラバラになりがち
ローカルに実行環境を構築する場合の問題点
- ローカルとリモートの実行環境で,ソフトウェアやライブラリのバージョンを合わせる手間の発生
- 編集したファイルを最終的にはアップロードしなければならない
ftpfs,sshfsなどでリモートディレクトリをローカルにマウントする際の問題点
- 接続先の環境や回線状況によってはエディタの動作が不安定になる場合がある
そこで,上記「2. サーバーにアップロード」のプロセスを自動化するために,「特定のsディレクトリにあるファイルが更新された直後に,バックグラウンドで対応するリモートホスト・ディレクトリにファイルがアップロードされる」VimScriptを書いてみました.Pythonを使っているので,お使いのVimが+pythonオプションを有効にしてコンパイルされている必要があります.
Vim6.1のVimScriptはデータ型として文字列と数字しかサポートされていないため,あまり複雑なことはやりにくいのに対して,Python拡張を使うとリスト(配列)や辞書(ハッシュ)が標準で使えるので,目的とする処理を書きやすいというメリットがあります.
autocmd BufWritePost * :call SyncFile() function! SyncFile() python << PYTHONEOF import vim import os upload_settings = { "/home/username/srcdir": "scp://hostname/public_html/", "/home/username/srcdir2": "scp://hostname2/public_html/", } fullpath = vim.current.buffer.name directory, filename = os.path.dirname(fullpath), os.path.basename(fullpath) if directory in upload_settings: vim.command("silent Nwrite "+upload_settings[directory]+filename) PYTHONEOF endfunction
スクリプト中のupload_settingsは各自の環境に合わせて変更してください.
という形式になっています.
この設定を.vimrcに追加すると,指定したディレクトリにあるファイルが更新(:w<CR>)された直後に,バックグラウンドで対応するリモートホスト・ディレクトリにファイルがアップロードされます.この結果としてローカルとリモートのディレクトリの同期を,リアルタイムかつシームレスにとることが可能になります.なお,scp://などのプロトコルはVimがサポートする範囲でftp://などに変更することが可能です.
ちなみに「3. (動作|表示)確認」のプロセスは,以下のma.la氏によるAutoReload.user.jsというGreaseMonkeyスクリプトで自動化できます.これを併用すると開発プロセスがさらに快適になるので,おすすめです
2007/11/08 3:10追記
コメント欄のns9tks氏による「Vim7では素のVimScriptでもリストや辞書が使える」というご指摘を受けて,Python拡張を使わないバージョンも作ってみました.こちらはVim7であれば+pythonオプション無しでコンパイルされたものでも動作するので,Vim7をお使いの場合はこちらがおすすめです.
autocmd BufWritePost * :call SyncFile() function! SyncFile() let upload_settings = { "/home/username/srcdir": "scp://hostname/public_html/" } let directory = expand("%:p:h") if has_key(upload_settings, directory) exec "silent Nwrite ".upload_settings[directory]."%" endif endfunction