本連載ではまだ取り上げていないが、Linuxのアドバンスドユーザーが好むツールの一つに「fzf」がある。fzfはファイルやディレクトリなどの曖昧選択を可能にするツールで、パスの一部を入力するだけでパスの選択を可能にしてくれる。絞り込みできるのはパスだけでなく、履歴やさまざまなデータが対象となっており、とにかく最大限に手を抜き、かつ、クールに仕事をするのに欠かせないツールだ。

fzfそのものについてはいずれ取り上げるとして、今回はこのツールをVimから使う方法を紹介しよう。本連載では、これまでファイルを開く方法として「Denite」と「NERDTree」を取り上げた。どちらも比較的ビジュアル化されたツールで、Visual Studio CodeのようなUI/UXでファイル選択が可能だった。タイピングやVimに慣れていないユーザーには扱いやすいプラグインではないかと思う。

一方、キー入力だけでもっと高速にファイル選択ができるやり方を好むユーザーも多い。結局、慣れてくると便利なUIは逆に操作が遅くなる原因にもなる。動作がわかっているなら、fzfのようなツールを使ったほうが高速な作業が可能だ。もしこれまでfzfを使ったことがなければ、ぜひ今回のプラグインで体験していただきたい。

fzfのインストールと設定

まず前回までに作成してきた設定ファイル~/.vimrcを確認しよう。次のようになっているはずだ。

"dein Scripts=============================
if &compatible
  set nocompatible               " Be iMproved
endif

" Required:
set runtimepath+=~/.cache/dein/./repos/github.com/Shougo/dein.vim

" Required:
if dein#load_state('~/.cache/dein/.')
  call dein#begin('~/.cache/dein/.')

  " Let dein manage dein
  " Required:
  call dein#add('~/.cache/dein/./repos/github.com/Shougo/dein.vim')

  " Add or remove your plugins here
  call dein#add('junegunn/seoul256.vim')
  call dein#add('vim-airline/vim-airline')
  call dein#add('vim-airline/vim-airline-themes')
  call dein#add('preservim/nerdtree')
  call dein#add('tpope/vim-commentary')
  call dein#add('tpope/vim-fugitive')
  call dein#add('fholgado/minibufexpl.vim')
  call dein#add('dense-analysis/ale')

  " Required:
  call dein#end()
  call dein#save_state()
endif

" Required:
filetype plugin indent on
syntax enable

" If you want to install not installed plugins on startup.
if dein#check_install()
  call dein#install()
endif

" seoul256
let g:seoul256_background = 233
colo seoul256

" vim-airline
let g:airline_powerline_fonts = 1
let g:airline_theme = 'molokai'

" NERDTree
"  <C-o> open NERDTree
nnoremap <silent> <C-o> :NERDTreeToggle<CR>

" minibufexpl
nnoremap <silent> bn :<C-u>:bnext<CR>
nnoremap <silent> b1 :<C-u>:b1<CR>
nnoremap <silent> b2 :<C-u>:b2<CR>
nnoremap <silent> b3 :<C-u>:b3<CR>
nnoremap <silent> b4 :<C-u>:b4<CR>
nnoremap <silent> b5 :<C-u>:b5<CR>
nnoremap <silent> b6 :<C-u>:b6<CR>
nnoremap <silent> b7 :<C-u>:b7<CR>
nnoremap <silent> b8 :<C-u>:b8<CR>
nnoremap <silent> b9 :<C-u>:b9<CR>

"End dein Scripts=========================

set number
syntax on
set whichwrap=b,s,[,],<,>,~,h,l
set cursorline
set incsearch
set hlsearch
set ignorecase

まずfzfのプラグインを読み込ませるために、「call dein#add(‘dense-analysis/ale’)」という記述の部分を次のように変更する。

  call dein#add('dense-analysis/ale')
  call dein#add('junegunn/fzf', {'build': './install --all'})
  call dein#add('junegunn/fzf.vim')

ここでちょっと説明が必要だ。fzfのプラグイン自体は「junegunn/fzf.vim」で、「junegunn/fzf」はfzfのコマンドそのもののリポジトリを指定している。fzf.vimは実際にfzfコマンドを実行して動作するためだ。上記の設定ではfzfをクローンしてビルドを行い、fzfコマンドを生成している。パッケージ管理システムを使ってfzfをインストールしてもよいのだが、設定の関係で上記のようにしておいたほうが簡単にセットアップが完了する。

次にfzfを使用するためのショートカットを追加する。設定ファイルで次の部分を見つけてほしい。

" minibufexpl
nnoremap <silent> bn :<C-u>:bnext<CR>
nnoremap <silent> b1 :<C-u>:b1<CR>
nnoremap <silent> b2 :<C-u>:b2<CR>
nnoremap <silent> b3 :<C-u>:b3<CR>
nnoremap <silent> b4 :<C-u>:b4<CR>
nnoremap <silent> b5 :<C-u>:b5<CR>
nnoremap <silent> b6 :<C-u>:b6<CR>
nnoremap <silent> b7 :<C-u>:b7<CR>
nnoremap <silent> b8 :<C-u>:b8<CR>
nnoremap <silent> b9 :<C-u>:b9<CR>

これを次のように変更する。

" minibufexpl
nnoremap <silent> bn :<C-u>:bnext<CR>
nnoremap <silent> b1 :<C-u>:b1<CR>
nnoremap <silent> b2 :<C-u>:b2<CR>
nnoremap <silent> b3 :<C-u>:b3<CR>
nnoremap <silent> b4 :<C-u>:b4<CR>
nnoremap <silent> b5 :<C-u>:b5<CR>
nnoremap <silent> b6 :<C-u>:b6<CR>
nnoremap <silent> b7 :<C-u>:b7<CR>
nnoremap <silent> b8 :<C-u>:b8<CR>
nnoremap <silent> b9 :<C-u>:b9<CR>

" fzf
nnoremap <silent> fzf :Files<CR>
nnoremap <silent> ls :Buffers<CR>

これで次のような操作が可能になる。

ショートカットキー 内容
fzf ファイルやディレクトリ一覧から選択
ls バッファ一覧から選択

fzf.vimでは主な機能として以下を提供している。上記の設定は、これらのうち「:Files」と「:Buffers」を呼び出すものだ。

コマンド 内容
:Files [PATH] ファイルやディレクトリ一覧から選択
:GFiles [OPTS] git ls-filesコマンド実行結果から選択
:GFiles? git statusコマンド実行結果から選択
:Buffers バッファ一覧から選択
:Colors カラースキーマ一覧から選択
:Ag [PATTERN] ar実行結果から選択
:Rg [PATTERN] rg実行結果から選択
:Windows ウィンドウ一覧から選択
:Locate PATTERN locateコマンド実行結果から選択
:History これまでに編集したファイルやバッファ一覧から選択
:History: コマンド履歴から選択
:History/ 検索履歴から選択

fzfを使うとファイルやディレクトリの選択のみならず、バッファやコマンド履歴などさまざまなものから選択を行うことができる。以前、「minibufexpl」を使ってバッファ一覧をタブのように表示する機能や、デフォルトの機能を使ってバッファ間を行き来する方法を説明したが、fzfの:Buffersを使えばバッファの一覧表示もそこからの選択もどちらもできる。慣れてきたらfzfのほうが素早く操作できるかもしれないので、ぜひ試してみていただきたい。

fzfプラグインの使用サンプル

実際にサンプルの使用例を見てみよう。ファイル編集中にノーマルモードで「fzf」と入力すると、下半分にfzf用のウィンドウが開いて、下部にキーワード入力用のプロンプトが表示されるようになる。右側に表示されているのは現在選択されているファイルの中身だ。

「:Files」を実行したところ

プロンプトに適当に文字を入力していくと、その文字をベースに曖昧検索が行われていき、表示される対象が絞り込まれていく。

キーワードに応じて絞り込みが進んでいく

最後まで文字を入力して絞り込むこともできるし、途中でカーソルキーの「↑」「↓」を使って表示されているリストからファイルを選択することもできる。

「↑」「↓」でリストから直接選択することもできる

次のスクリーンショットは「ls」でバッファ一覧を表示させたところだ。曖昧検索でさらに絞り込むこともできるし、一覧から選択してバッファを開くこともできる。

「:Buffers」を実行したところ

fzfを使いこなすことができるとファイル選択のストレスをかなり減らすことができる。ただし、fzfはかなり好みがはっきり分かれる機能だということも言っておきたい。肌に合わない人は、この手の曖昧な操作はまるで受け付けないのではないかと思う。

しかし、無駄な入力を省いて目的のファイルに到達する方法として、fzfはこの上ないことは間違いない。使ったことがないのであればぜひ一度体験してみていただきたい。

今回の成果物

今回のプラグイン設定を追加した設定ファイルは以下の通りだ。

"dein Scripts=============================
if &compatible
  set nocompatible               " Be iMproved
endif

" Required:
set runtimepath+=~/.cache/dein/./repos/github.com/Shougo/dein.vim

" Required:
if dein#load_state('~/.cache/dein/.')
  call dein#begin('~/.cache/dein/.')

  " Let dein manage dein
  " Required:
  call dein#add('~/.cache/dein/./repos/github.com/Shougo/dein.vim')

  " Add or remove your plugins here
  call dein#add('junegunn/seoul256.vim')
  call dein#add('vim-airline/vim-airline')
  call dein#add('vim-airline/vim-airline-themes')
  call dein#add('preservim/nerdtree')
  call dein#add('tpope/vim-commentary')
  call dein#add('tpope/vim-fugitive')
  call dein#add('fholgado/minibufexpl.vim')
  call dein#add('dense-analysis/ale')
  call dein#add('junegunn/fzf', {'build': './install --all'})
  call dein#add('junegunn/fzf.vim')

  " Required:
  call dein#end()
  call dein#save_state()
endif

" Required:
filetype plugin indent on
syntax enable

" If you want to install not installed plugins on startup.
if dein#check_install()
  call dein#install()
endif

" seoul256
let g:seoul256_background = 233
colo seoul256

" vim-airline
let g:airline_powerline_fonts = 1
let g:airline_theme = 'molokai'

" NERDTree
"  <C-o> open NERDTree
nnoremap <silent> <C-o> :NERDTreeToggle<CR>

" minibufexpl
nnoremap <silent> bn :<C-u>:bnext<CR>
nnoremap <silent> b1 :<C-u>:b1<CR>
nnoremap <silent> b2 :<C-u>:b2<CR>
nnoremap <silent> b3 :<C-u>:b3<CR>
nnoremap <silent> b4 :<C-u>:b4<CR>
nnoremap <silent> b5 :<C-u>:b5<CR>
nnoremap <silent> b6 :<C-u>:b6<CR>
nnoremap <silent> b7 :<C-u>:b7<CR>
nnoremap <silent> b8 :<C-u>:b8<CR>
nnoremap <silent> b9 :<C-u>:b9<CR>

" fzf
nnoremap <silent> fzf :Files<CR>
nnoremap <silent> ls :Buffers<CR>

"End dein Scripts=========================

set number
syntax on
set whichwrap=b,s,[,],<,>,~,h,l
set cursorline
set incsearch
set hlsearch
set ignorecase