vi[m] like file manager な vifm の紹介と vifmrc 設定例

本記事は [Vim Advent Calendar 2019 \- Qiita](https://qiita.com/advent-calendar/2019/vim) の 17 日目の記事です。

この記事では [vifm](https://vifm.info/) についてご紹介します。

<img width="845" alt="capture_vifm_for_advent_calendar_2019.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46167/65ef11ea-140e-92eb-e788-78e6c81f2ddc.png">

## vifm とは

vifm とは ncurses ベースで vi\[m\] ライクな、ターミナル内で操作できるファイラ（ファイルマネージャ）の一つです。おそらく vi\[m\] (like) file manager の略で vifm なのだと思います。

- [Vifm](https://vifm.info/)
- [Vifm (GitHub)](https://github.com/vifm)

ncurses は TUI (Text User Interface) を構築する際によく用いられるライブラリで、以前こちらで紹介した Tig など、多くのソフトウェアで使われています。

- [Tig で Git を自由自在に操作するための \.tigrc 設定例 \- Qiita](https://qiita.com/sfus/items/063797a1dd8fdc7d032f)

## 自分とファイラ

少しだけ自分の話をすると、昔から 2 画面ファイラと呼ばれるものを長年愛用していました。2 画面ファイラとは左右にファイル一覧が表示されるファイルマネージャで、コピーや移動などの操作を左のペインから右のペインに (あるいは右のペインから左のペインに) 向けて実行でき、高速なファイル操作が行えるのが特徴です。

以前 Windows 機をメインにしていた際は [２画面ファイラーKF](http://www.kissy-software.com/kf/) や [だいなファイラー](http://hp.vector.co.jp/authors/VA004117/dyna.html) などを愛用していました。

作業環境を Mac に変更してからは、Java で開発されている [muCommander](http://www.mucommander.com/) を使用していました。その後メンテナンスが滞ったタイミングで、 GPL の muCommander のコードを fork して開発を継続している trolsoft の [trolCommander](https://trolsoft.ru/en/soft/trolcommander) を使用していました。

trolCommander は概ね満足いくファイラでしたが 、GUI ソフトウェアなので自分の様に git 操作に Tig を使っていたり、普段からターミナルで作業することが多い人間は少し手間に感じていました。そのため、ターミナル内で実行できる 2 画面ファイラで、かつ操作性や拡張性がよいものを長年探していました。

そして数年前に vifm を使い始めてから、 trolCommander と同等以上に自分のニーズを満たしてくれることを確認できたので移行しました。今では手放せない存在になっています。

以下では、 vifm の初期設定での操作方法と、便利な設定について説明していきます。

ちなみに vim でのファイラは色々と有名なプラグインがあります(※)が、vifm 本体は vim を起動せずにスタンドアロンで起動します。それとは別に、プラグインで vim から呼び出す方法もあります (後述) 。起動は非常に速く、一瞬で立ち上がります。

※ 参考:mattn さんのファイラ記事

- [Big Sky :: Vim 上のファイラを NERDTree から dirvish に乗り換えた。](https://mattn.kaoriya.net/software/vim/20150310202155.htm)


## vifm のインストール方法

公式ページ ([Vifm \- Downloads](https://vifm.info/downloads.shtml)) にバイナリがあります。また各種OSやディストリビューション向けにパッケージも用意されています。

Mac ユーザであれば

```
$ brew install vifm
```

で簡単にインストールできます。

## 基本的な操作方法

ここで紹介する内容はすべて公式マニュアルに書かれているので、詳細については本家ドキュメントをご確認ください。便利な Cheetsheet もあります。

- [Vifm \- Manual](https://vifm.info/manual.shtml)
- [Vifm \- Cheatsheet](https://vifm.info/cheatsheets.shtml)


### 終了方法

定番のコマンドですが、 vim と同じ操作で終了できます。

|キー| 説明             |
|:-:|-----------------|
| :q | vifm を終了する |
| ZZ | vifm を終了する |
| ZQ | エラーコードで vifm を終了する (後述) |

(※ ただし毎回これを打つのは手間なので、自分は `q` に vifm 終了コマンドを割り当てています (後述))

### カーソル移動

|キー| 説明             |
|:--:|-----------------|
| j | カーソルを下に移動 |
| k | カーソルを上に移動 |
| h | ひとつ上のディレクトリに移動 |
| l | カーソル配下がディレクトリならそのディレクトリに移動する。ファイルならファイルをエディタで開く (デフォルトは vim )  |
| gg | カーソルを最上段に移動 |
| G | カーソルを最下段に移動 |
| \[SPACE\] | 反対側のペインに移動 |
| \[TAB\] | 反対側のペインに移動 |
| Ctrl-i | 反対側のペインに移動 |

その他のキーバインドは [マニュアルの Basic Movement](https://vifm.info/manual.shtml#Basic%20Movement) を参照してください。


### ファイル操作

|キー| 説明             |
|:--:|-----------------|
| e | 現在のペインでファイルを表示する |
| yy | ファイルを vifm のクリップボードにコピー (yank) する |
| Y  | ファイルを vifm のクリップボードにコピー (yank) する |
| C | ファイルをクローンする (同じディレクトリに `ファイル名(1).拡張子` という名前で複製する) |
| p | yank したファイルをコピーする。または `dd` で削除したファイルをコピーする |
| P | yank したファイルを移動する (元の場所からは消える) |
| dd | ファイルを vifm の trash ディレクトリに入れる (`p` で貼り付けられる) |
| DD | ファイルを完全に削除する |
| cw | ファイルをリネームする |
| cW | ファイルをリネームする (拡張子を除く) |
| al | yank したファイルの symbolic link を絶対パスで作成する |
| rl | yank したファイルの symbolic link を相対パスで作成する |
| t | カーソル配下のファイル/ディレクトリを選択状態にする |

trash ディレクトリについては[マニュアルの Trash directory](https://vifm.info/manual.shtml#Trash%20directory) をご参照ください。筆者のデフォルトでは `~/.local/share/Trash` でした。

## カスタマイズ

上記のコマンドを覚えておけば最低限のファイル操作はできると思いますが、やはり手に馴染むようにカスタマイズしてこそのファイラなので、カスタマイズの方法をご紹介します。

設定ファイルのロード順については[マニュアルの Startup](https://vifm.info/manual.shtml#Startup) に書かれています。最優先は `~/.vifm/vifmrc` ファイルですが、インストール直後にはこのファイルは存在しないので `~/.config/vifm/vifmrc` ファイルが読み込まれていると思います。このファイルは以下のいずれかがインストールされています。

- [vifm/vifmrc at master · vifm/vifm](https://github.com/vifm/vifm/blob/master/data/vifmrc)
- [vifm/vifmrc\-osx at master · vifm/vifm](https://github.com/vifm/vifm/blob/master/data/vifmrc-osx)

まずはこのディレクトリをコピーしてカスタマイズを始めてみます。

```
$ cp -a ~/.config/vifm ~/.vifm
```

### 表示変更

デフォルトの画面だと情報量が少なすぎるので、だいたい以下のようにしています。それぞれの詳細については [マニュアルの :set options](https://vifm.info/manual.shtml#:set%20options) をご確認ください。

```~/.vifm/vifmrc
set timefmt=" %Y/%m/%d %H:%M:%S"
set statusline="  %t  [%T]%= %A %10u:%-7g %15s %20d [%a free]"
set viewcolumns=-45%{name}..,-{ext},10{size},12{perms},21{mtime}
set sizefmt=units:iec,precision:3
```

また、執筆当時最新の ver 0.10.1 を入れてみたところ、カラースキームが微妙になっていたので修正しています。カラースキームはデフォルトでは `colorscheme Default` が指定されているため `~/.vifm/colors/Default.vifm` が読まれます（前述の `~/.config/vifm` ディレクトリをコピーしている場合）。

カラースキームは [color_scheme.c](https://github.com/vifm/vifm/blob/v0.10.1/src/ui/color_scheme.c) で動的に作られているようですが、自分の場合 `OtherWin` の設定値が目立ちすぎていたのでコメントアウトしています。

```~/.vifm/colors/Default.vifm
" highlight OtherWin cterm=bold,underline,reverse,standout ctermfg=default ctermbg=default
```

本記事の最初にも貼りましたが、上記を設定した際の筆者の iTerm2 (色調整した Solarized Dark テーマ) での vifm のスクリーンショットはこのような感じです。（右下のカレントパーティションの空き容量 `[%a free]` の値はうまくとれていないようですが）

<img width="845" alt="capture_vifm_for_advent_calendar_2019.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46167/65ef11ea-140e-92eb-e788-78e6c81f2ddc.png">


ちなみに vifm-colors というサブプロジェクトにも各種カラースキームが置かれています。

- [vifm/vifm\-colors: Various colorschemes for vifm file explorer\.](https://github.com/vifm/vifm-colors)

これらのカラースキームごとのサンプルは公式サイトに一覧があります。

- [Vifm \- Color Schemes](https://vifm.info/colorschemes.shtml)


### エディタ/ビューア変更

`l` でファイルをチラ見するときに毎回 vim が開くと閉じるのが手間なので `less` に変えています。

```~/.vifm/vifmrc
set vicmd='less -N'
```

(`-N` は `--LINE-NUMBERS` のことで行数表示のオプションです)

また、 `h` キーで `less` を抜けられるように、 `lesskey.conf` を以下のように書いて

```lesskey.conf
h   quit
```

```
$ lesskey -o ~/.lesskey lesskey.conf
```

を実行することで `less` も `h` キーで抜けられるようになります。

### コマンド定義

[マニュアルの Commands](https://vifm.info/manual.shtml#Commands) に書かれているコマンドを（ノーマルモードの）キーにバインドする場合は、 vim の `.vimrc` と同様に

```~/.vifm/vifmrc
nnoremap C :copy
```

の様に書きます（デフォルトの `C` キーを上書きしています）。 `:copy` は組み込みコマンドで、反対側のペインにコピーを行います。

前述した、 `q` で vifm を抜けるようにするには

```~/.vifm/vifmrc
nnoremap q :quit<cr>
```

と書きます。(`<cr>` はコマンド行で確認せずに Enter します)

### 外部コマンド呼び出し

組み込みコマンド以外をシェル経由で実行するには、 `!` をつけます (`%` 引数については後述します)

```~/.vifm/vifmrc
nnoremap Z :!zip -r %D/%c:r.zip %f
```

また、コマンドを定義しておいて、内部コマンドの様に読み出すこともできます。

```~/.vifm/vifmrc
command! zip -r %D/%c:r.zip %f
nnoremap Z :zip
```

そのほか、前述のとおり `l` キーによる `vicmd` を変更してしまっているので、改めて vim に割り当て直す場合は次のように書きます。

```~/.vifm/vifmrc
command! vim vim %f
nnoremap E :vim<cr>
```


### コマンド引数

例えば unzip コマンドを `U` に割り当てたい場合、

```~/.vifm/vifmrc
nnoremap U :!unzip %f -d %D
```

と書くことでコマンド引数を展開することができます。ここで `%f` はカーソル配下のファイルパス、 `%D` は反対側ペインのディレクトリを指します。このあたりの変数の詳細は [マニュアルの Command macros](https://vifm.info/manual.shtml#Command%20macros) や、設定ファイル [~/.vifm/vifmrc](https://github.com/vifm/vifm/blob/v0.10.1/data/vifmrc#L124-L134) に書かれています。


### 仮想ディレクトリ

一般にファイラは、 zip などのアーカイブも展開せずに中を開くことができるのが一般的です。vifm で zip ファイルを仮想ディレクトリとして開くには [FUSE](https://ja.wikipedia.org/wiki/Filesystem_in_Userspace) を利用します。

Mac OS の場合は、[Home \- FUSE for macOS](https://osxfuse.github.io/) からダウンロード&インストールするか、 Homebrew Cask が利用可能な場合は以下のコマンドでインストールできます。

```
$ brew cask install osxfuse
```

さらに zip ファイルを開くために fuse-zip を入れます。 [公式サイト](https://bitbucket.org/agalanin/fuse-zip/wiki/Home) からダウンロード&インストールするか、Mac では Homebrew でインストールできるので、以下のコマンドでインストールしてください。

```
$ brew install fuse-zip
```

そして `*.zip` やその他の対応ファイルタイプに対して fuse-zip を割り当てます。

```~/.vifm/vifmrc
filetype *.zip,*.jar,*.war,*.ear FUSE_MOUNT|fuse-zip %SOURCE_FILE %DESTINATION_DIR
```

と書いてみて気づきましたが、執筆当時最新の [vifm v0.10.1 の初期設定ファイル](https://github.com/vifm/vifm/blob/v0.10.1/data/vifmrc#L282-L289) には fuse-zip がある場合は自動で上記設定が有効になるようになってたので、現状では特に設定は不要となっているようです。


### その他の FUSE Mount

その他、 `sshfs` や `curlftpfs` などの FUSE mount の記述が [初期設定](https://github.com/vifm/vifm/blob/v0.10.1/data/vifmrc#L292-L326) にあります。


sshfs については、

```
$ brew install sshfs
```

のようにインストールしたのち、

```hoge_server.ssh
username@hoge.example.com:/home/username
```

と `.ssh` と拡張子を持つファイルに `sshfs` に渡す接続情報を書いておけば、 `l` キーでそのファイルを開こうとすると透過的に ssh 先のサーバを手元の vifm で開くことができます。ローカルのコンテナの接続情報を書いていても便利だと思います。


### vifm 起動ショートカット

ターミナルでは vifm か tig かエディタを開いて生活しているので、コマンド一つで vifm が開けると便利です。

zsh の場合は `~/.zshrc` に以下の設定を入れておけば `Alt-j` で vifm をカレントディレクトリですぐに開けます。

```~/.zshrc
bindkey -s '\ej' '^a vifm . \n'
```

（history に残さないように、頭に空白を入れています。これを有効にするには `setopt hist_ignore_space` を `.zshrc` に記載しておいてください）


### 起動シェルのカレントディレクトリ変更

vifm を抜けた際に、最後のディレクトリを呼び出したシェルのカレントディレクトリとして欲しいケースが多いと思います。
[公式マニュアルにその設定](https://wiki.vifm.info/index.php?title=How_to_set_shell_working_directory_after_leaving_Vifm) が紹介されています。

以下は `~/.zshrc` の例です。（公式マニュアルには `.bashrc` の設定が載っています）

```~/.zshrc
function vicd() {
    local dst="$(command vifm $1 $2 --choose-dir -)"
    if [ -z "$dst" ]; then
        echo 'Directory picking cancelled/failed'
        return 1
    fi
    cd "$dst"
}
```

これで `vicd` コマンドで実行された vifm が正常終了 (`:q` or `ZZ`) の場合は、 vifm の最後のディレクトリがカレントディレクトリに設定されます。

こちらを普段使うため、上記 `Alt-j` コマンドは vicd に書き換えています。

```~/.zshrc
bindkey -s '\ej' '^a vicd . \n'
```

また、 `ZQ` で抜けた場合は起動時のディレクトリのままになります。

ちなみに自分の場合、`q` と `Q` をそれぞれに割り当て直しています。

```~/.vifm/vifmrc
nnoremap q :quit<cr>
nnoremap Q :cquit<cr>
```

### その他便利なコマンドのキー割り当て

以下は筆者が設定しているキー割り当ての一部です。これまでに説明していないもののうち、特に設定しておいたほうが便利だと思うものを挙げてみます。（キーバインドはお好みのものに変更してください)

```~/.vifm/vifmrc
" ディレクトリを作成する
nnoremap K :mkdir<space>

" カレントファイルを関連付けられたデフォルトのアプリケーションで開く。フォルダの場合は Finder で開く (Mac)
nnoremap X :!open %f<cr>

" 反対側のペインをカレントペインと同期する
nnoremap , :sync!<cr>

" スペースキーを「カレントファイルを選択して、カーソルを1つ下に移動」にする
nnoremap <space> t<down>
```

（上記以外については[筆者の vifmrc](https://github.com/sfus/dotfiles/blob/vifm-for-qiita-advent-calendar-2019/terminal/vifm/vifmrc) を参照してください）


### その他

AFP (Mac のファイル共有プロトコル) でマウントしたサーバに対して `:move` コマンドによるファイルの移動をすると更新日時が保持されないバグ（仕様？）があったので（現バージョンで解消されているかは未確認ですが）リモートサーバを操作する場合は事前に動作検証をしておくとよいと思います。


## Vim 連携

ここまではスタンドアロンの vifm について説明してきましたが、 vim から vifm を呼び出すプラグインも存在しています。

- [vifm/vifm\.vim: Vim plugin that allows use of vifm as a file picker](https://github.com/vifm/vifm.vim)
- [vifm\.vim \- Vim Awesome](https://vimawesome.com/plugin/vifm-vim-sparks-fly)

Vundle の場合、 `call vundle#begin()` ~ `call vundle#end()` 間に以下の指定をして `:PluginInstall` でインストールされます。

```~/.vimrc
Plugin 'vifm/vifm.vim'
```

プラグインのドキュメントによると以下のコマンドが定義されています。([マニュアルの Plugin](https://vifm.info/manual.shtml#Plugin) にも同様の説明があります)

- [vifm\.vim/vifm\-plugin\.txt at master · vifm/vifm\.vim](https://github.com/vifm/vifm.vim/blob/master/doc/plugin/vifm-plugin.txt)


```
Commands:

                                               *vifm-:EditVifm*
  :EditVifm   select a file or files to open in the current buffer.
                                               *vifm-:Vifm*
  :Vifm       alias for :EditVifm.
                                               *vifm-:SplitVifm*
  :SplitVifm  split buffer and select a file or files to open.
                                               *vifm-:VsplitVifm*
  :VsplitVifm vertically split buffer and select a file or files to open.
                                               *vifm-:DiffVifm*
  :DiffVifm   select a file or files to compare to the current file with
              :vert diffsplit.
                                               *vifm-:TabVifm*
  :TabVifm    select a file or files to open in tabs.
```

上記設定例で `vicmd` 変数を `less` にして `l` キーを `less` で開くようにしていましたが、 vim plugin から呼び出すと、 `less` ではなく vim で開かれるようになります。


## まとめ

本記事では、 Vim ベースのファイルマネージャ vifm をご紹介しました。皆様のターミナル生活の一助になれば幸いです。

最後までお読みいただきありがとうございました。