101
95

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

VimAdvent Calendar 2015

Day 14

Vim で引き籠る

Last updated at Posted at 2015-12-13

2015年総括

今年も沢山、良い Vim プラグインが誕生しました。
また皆さんからも幾度か vim-jp に vim の不具合報告を頂き、vim-dev にパッチとして還元する事が出来ました。本当にありがとうございました。さらに個人的には技術評論社出版の「Software Design」で連載記事「Vim の細道」を執筆させて頂く事になり1、自他共にビムビムしい1年だったと思います。:book: しかし今後も皆さんが使うテキストエディタは皆さん自身が考えて良くしていく、そういう気持ちを持ちながら引き続き Vim 活動を続けて行きたいと思います。

さて 2015年は如何だったでしょうか。Vimmer になりたいと思っている人たちは Vimmer になれたでしょうか。Vimmer の世間一般のイメージと言えば

  • vimrc ばかり弄っている
  • プラグインばかり作っている
  • 現代でも vim が最強だと思ってる
  • 引き籠っている

悔しいですが、あながち間違いではないですね(え)。

「vimrc ばかり弄っている」... ぜひ弄って下さい!
「プラグインばかり作っている」... ぜひ作ってください!
「現代でも vim が最強だと思ってる」... 僕もそう思います!
「引き籠っている」... 引き籠りましょう!

そうだ、引き籠ろう

周りの人からすると、コソコソやっている様に見えるかもしれません。:sun_with_face:

実際そうかもしれません。しかしそういったコソコソから生まれる新しい技術もあるのです。
Git でファイルを管理するのもいいけど、出来れば直接ファイルを弄りたい。そして直接書き込めるの、カッコイイ!(中二病)

ならば書いたらいいじゃない!
ネットワークの向こう側の、俺たちのファイルを直接
:w で保存したらいいじゃない! (BGMスタート)

ネットワークを超えろ

Vim には Netrw というプラグインが標準で同梱されています。HTTP であれば読み取り専用でリソースを開く事が出来ます。また FTP や SCP も使えます。さらに WebDAV もサポートしています。

しかしこの WebDAV を読み書きする機能、実際は cadaver というコマンドラインプログラムを使っており、Windows からだと Cygwin を使わない限り扱う事が出来ません。Cygwin 嫌いの僕にとってはとても苛立ちを覚えます。:thumbsdown:

それでも僕は Windows の Vim から WebDAV 上のファイルを読み書きしたい

Windows から WebDAV 上のファイルを読み書きする方法が無い訳ではありません。それどころか Windows にはネットワークプレースという機能があり、エクスプローラから登録する事で UNC パスとして参照する事ができ、さらにネットワークドライブとして登録する事も可能です2。コマンドプロンプトは UNC パスをカレントディレクトリとして起動する事が出来ないため Windows を UNIX っぽく扱いたい人にとっては色々と問題が発生しますが、ネットワークドライブとして割り当てる事で通常のドライブとして扱える様になります。:beers:

  1. ネットワークプレースを追加する
    box.com を WebDAV として扱う

  2. Basic 認証に Box.com のアカウント情報を入力する
    box.com の Basic 認証

  3. エクスプローラから参照可能になる
    box.com がネットワークプレースに追加される

  4. UNC パスとしても参照可能
    UNC パスとして参照可能

ネットワークプレースをネットワークドライブとして割り当てる場合は以下を実行します。

net use * https://dav.box.com/dav

ただし作成したネットワークプレースをエクスプローラから削除した場合、接続情報がキャッシュされたままになり次回登録する際にエラーが発生してしまいます。その場合は以下のコマンドを実行して解除して下さい。

net use \\dav.box.com@SSL\dav /delete

負けた感じがする

でもこれ、完全に Windows の機能に乗っかってしまっていて、とても負けた感じがしますね。新しい接続先を得るたびに毎回この設定が必要です。同じ方法で Linux や Mac OS X から実現できませんし、異なる環境で手法や vimrc を共有したい Vimmer からすると完全に敗北です。だからといって Cygwin 版の cadaver を使う気にはなれないのです。大事な事なのでもう一度言います。

なれないのです!

ならば作ってしまおうと思ったのです。Vim プラグインから扱いやすい WebDAV クライアントを作ってしまおう。そう思ったのです。


WebDAV とは

RFC 2518 で定義されている HTTP の拡張プロトコルで、通常の GET/HEAD/PUT/POST/DELETE といったメソッド以外に以下の拡張メソッドを使用します。

メソッド 説明
PROPFIND プロパティの取得
PROPPATCH プロパティの変更
MKCOL ディレクトリ(コレクション)の作成
COPY リソースの複製
MOVE リソースの移動
LOCK リソースのロック
UNLOCK リソースのロック解除

また GET/HEAD/PUT/POST/DELETE についてはリソースの取得や作成、更新、削除に用いられます。

通常の WebDAV クライアントは接続後に OPTIONS メソッドを発行し、サーバがどのメソッドをサポートしているかを調べます。


$ curl -X OPTIONS -u XXX:XXX -s -i https://dav.box.com
HTTP/1.1 200 OK
Server: ATS
Date: Fri, 11 Dec 2015 02:17:38 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 0
Accept-Ranges: bytes
MS-Author-Via: DAV
DAV: 1, 3, extended-mkcol, 2
Allow: OPTIONS, GET, HEAD, DELETE, PROPFIND, PUT, PROPPATCH, COPY, MOVE, REPORT,
 LOCK, UNLOCK
Vary: Accept-Encoding
Age: 0
Connection: keep-alive

次にパスを指定して PROPFIND メソッドを実行します。その際、以下のプロパティを要求します。

<d:propfind xmlns:d="DAV:">
  <d:prop>
    <d:displayname/>
    <d:resourcetype/>
    <d:getcontentlength/>
    <d:getlastmodified/>
  </d:prop>
</d:propfind>

ファイルシステムとしての属性が返却されます。

$ curl -X PROPFIND --data @req.xml -u XXX:XXX -s -i https://dav.box.com/dav/
HTTP/1.1 207 Multi-Status
Server: ATS
Date: Fri, 11 Dec 2015 02:24:05 GMT
Content-Type: application/xml; charset=utf-8
Content-Length: 3507
DAV: 1, 3, extended-mkcol, 2
Vary: Accept-Encoding
Age: 0
Connection: keep-alive
<?xml version="1.0" encoding="utf-8"?>
<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
  <d:response>
    <d:href>/dav/</d:href>
    <d:propstat>
      <d:prop>
        <d:resourcetype><d:collection/></d:resourcetype>
        <d:getcontentlength>725280</d:getcontentlength>
        <d:getlastmodified xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">Fri, 11 Dec 2015 02:24:05 GMT</d:getlastmodified>
      </d:prop>
      <d:status>HTTP/1.1 200 OK</d:status>
    </d:propstat>
    <d:propstat>
      <d:prop><d:displayname/></d:prop>
      <d:status>HTTP/1.1 404 Not Found</d:status>
    </d:propstat>
  </d:response>
  <d:response>
    <d:href>/dav/Documents/</d:href>
    <d:propstat>
      <d:prop>
        <d:resourcetype><d:collection/></d:resourcetype>
        <d:getcontentlength>0</d:getcontentlength>
        <d:getlastmodified xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" b:dt="dateTime.rfc1123">Fri, 15 Jun 2007 07:38:29 GMT</d:getlastmodified>
      </d:prop>
      <d:status>HTTP/1.1 200 OK</d:status>
    </d:propstat>
    <d:propstat>
      <d:prop><d:displayname/></d:prop>
      <d:status>HTTP/1.1 404 Not Found</d:status>
    </d:propstat>
  </d:response>
</d:multistatus>

この様に WebDAV では属性の要求と応答を XML を使ってやり取りし、リソースの操作は GET/PUT/POST/DELETE といった通常の HTTP メソッドを使う事で実現しています。詳しくは RFC 2518 を参照して下さい。

WebDAV クライアントを作ろう

閑話休題。要件としては

  • readline っぽく動作してコマンドをバシバシと叩けて
  • 出来れば色付きでファイル一覧が表示されて
  • それでいて Windows からも使えて
  • Vim プラグインからも扱える物(ちょっと!ここが本題よ)

となります。上記の要件を満たせるプログラミング言語としては golang が最適であろうという事で今回も golang で作りました。

davc

https://github.com/mattn/davc

インストールは以下の様に実行します。

$ go get github.com/mattn/davc

WebDAV プロトコル実装部分は gowebdav を使用しています。liner を使っており、リモートでもローカルでもファイル名補完も効いて、とても使いやすくなっていると思います。扱えるコマンドは以下の通りです。

コマンド 説明
lpwd ローカルのカレントディレクトリを表示
pwd リモートのカレントディレクトリを表示
lcd ローカルのカレントディレクトリを変更
cd リモートのカレントディレクトリを変更
lmkdir ローカルにディレクトリを作成
mkdir リモートにディレクトリを作成
lls ローカルのファイル一覧を表示
ls リモートのファイル一覧を表示
ll リモートのファイル詳細一覧を表示
lrm ローカルのファイルを削除
rm リモートのファイルを削除
lrmdir ローカルのディレクトリを削除
rmdir リモートのディレクトリを削除
put ファイルをリモートに転送
get ファイルをローカルに転送
cp リモートでのファイルコピー
mv リモートでのファイル移動
cat リモートのファイルをcat
write 標準入力の内容をリモートに転送
edit/vim リモートのファイルをvimで編集し更新
exit 終了

これだけで記事を終えても良いのですが(良くない)、気付いたらこれ Go Advent Calendar 2015 じゃなくて Vim Advent Calendar 2015 だったので これを Vim から扱えるプラグインを作る必要があります。こういった用途に使えるのがお馴染み metarw です。

https://github.com/kana/vim-metarw

metarw の拡張プラグインとして metarw-webdavというのを作りました。

vim-metarw-webdav

https://github.com/mattn/vim-metarw-webdav

これを使えば

:e webdav://server/path/to/file.txt

といった様にリモートのファイルを直接読み込み、編集し、:w で保存する事が出来ます。Basic 認証が掛かっている場合は

:e webdav://user:password@server/path/to/file.txt

でアクセスするか davc 用の環境変数 DAVC_CREDuser:pass の形式で設定して下さい。

ようやく Vim から WebDAV 上のファイルを読み書きできる様になりました。おしまい。

WebDAV 扱えるサーバ、実はそんなに無い

しかし WebDAV サーバって構築がそれほど簡単じゃない。僕もこの davc や metarw-webdav を作っていて薄々と感じ取っていました。またオンラインストレージに関しても WebDAV が扱える有名どころのファイルストレージサーバと言えば Box.com くらいしか無い様に思います。(もし知ってたら教えて下さい)

ならば作ったらいいんじゃね?:knife:
そう思った僕は、迷いもなく Vim でソースを書き始めていました。

https://github.com/mattn/davfs

インストールは以下の様に行います。

$ go get github.com/mattn/davfs/cmd/davfs

同じく golang で書いた WebDAV サーバです。リソースの管理方式は

  • ファイル
  • メモリ
  • データベース

のどれかを選択出来ます。データベースを使う場合はサーバ側に同じファイルシステムが構築される訳ではありません。あくまでデータベース内にファイルシステムが構築されます。

引き籠り度が強くてとても良いですね。


ここでちょっと休憩

実は golang には x/net/webdav という webdav を扱う為のパッケージが用意されています。WebDAV 上のリソースが os.FileInfo を使って透過的に表現されています。

webdav/file.go
// A FileSystem implements access to a collection of named files. The elements
// in a file path are separated by slash ('/', U+002F) characters, regardless
// of host operating system convention.
//
// Each method has the same semantics as the os package's function of the same
// name.
//
// Note that the os.Rename documentation says that "OS-specific restrictions
// might apply". In particular, whether or not renaming a file or directory
// overwriting another existing file or directory is an error is OS-dependent.
type FileSystem interface {
	Mkdir(name string, perm os.FileMode) error
	OpenFile(name string, flag int, perm os.FileMode) (File, error)
	RemoveAll(name string) error
	Rename(oldName, newName string) error
	Stat(name string) (os.FileInfo, error)
}

ですので webdav.FileSystem インタフェースに合うように実装を行えば、それだけで WebDAV 上のファイルシステムとして投入出来る様になっています。

webdav パッケージには、デフォルトでファイルシステムを表した Filesystem (そのままやんけ)と、メモリファイルシステムが用意されており、実際のファイルシステムを使って WebDAV したい人がいれば直ぐにでもサーバを実装出来る様になっています。:sushi:


さて davfs ですが、扱えるデータベースは以下の3つです。

  • SQLite3
  • PostgreSQL
  • MySQL

試してないのでこれは推測ですが、おそらく PostgreSQL を使えば Heroku 上に不揮発な WebDAV を作る事も出来るかと思います。SQLite3 については go-sqlite3 を使っているのでビルドすると静的リンクされたコマンドが生成でき、USB メモリに仕込ませておけば複数人数で扱う作業の中で「どこでもファイルシステムー!」的な事が出来て意外と便利かもしれません。

ウェーブダーブー!

起動方法は以下の通り。

$ davfs -driver sqlite3 -source foo.db

driver と source を指定して起動します。MySQL(ドライバ名:mysql) や PostgreSQL(ドライバ名:postgres) のソース指定は各ドライバの接続文字列に従って下さい。なお初回のみ以下を実行してデータベース上にファイルシステムを作成する必要があります。

$ davfs -driver sqlite3 -source foo.db -create

実際には以下のテーブルと初期レコードが作成されます。

create table filesystem(
	name text not null, 
	content text not null,
	mode bigint not null,
	mod_time timestamp not null,
	primary key (name)
);

insert into filesystem(name, content, mode, mod_time) values('/', '', 2147484159, current_timestamp);

content はバイナリが扱える様に Hex 文字で格納します。本当は Blob で扱いたかったのですが、ファイルシステム上でファイルを追加モードで開いて何度か書き込む場合、Blob の結合を行う必要があるのですが、その際ローカル側に追加前のコンテンツを持ってくるとメモリをアロケーションが多発するので(SQLだけで完結したかった)この様な形式になりました3。簡単なファイルシステムなのでやろうと思えば SQL だけでファイルを作成する事も出来るかもしれませんね。
ファイル、もしくはディレクトリが1行のレコードで格納されます。ファイル名をキーにしている為、例えばディレクトリ内のファイル検索であればディレクトリ名を LIKE で前方一致させる SQL でファイル検索が出来ます。

ただし MySQL の場合は残念な事にインデックスに text 型を使うと255バイトまでしか扱えない為、結果ファイル名が255バイトまでしか扱えなくなります。:gun:

とは言えファイルシステム上で画像ファイルを上書き保存したり、動画ファイルをコピーしたりもしてみましたが、ひとまず問題無いようです。

先に説明したファイルシステム Filesystem と、メモリ Filesystem も扱える様にしてありますので、データベースを用意しなくても社内 LAN 内で何時でも簡単に WebDAV サーバがコマンド1つで起動出来ます4

まとめ

davc や davfs、はたまた vim-metarw-webdav により Vim から簡単に WebDAV 上のファイルを読み書き出来る様になりました。また Linux であれば davfs2 を使えば簡単に webdav 上のファイルシステムをマウントする事が出来ますし、Windows でも先に説明した様にネットワークプレースを使う事で簡単に WebDAV を扱う事が出来ます。コソコソと、新しい技術を身に着け、無事に引き籠った後には大きなアウトプットを皆に見せて下さい。

おしまい。

  1. http://mattn.kaoriya.net/software/vim/20150820112724.htm

  2. https://technet.microsoft.com/ja-jp/library/ee155455.aspx

  3. 実際には SQLite3 だけ Blob の結合が SQL で出来なかった。

  4. もちろんメモリファイルシステムで起動すると終了時にファイルが消え去ります。

101
95
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
101
95

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?