四 月
30
日曜日

fzfによるCUI生活の改善

「この休みがあればやりたかった事が何でも出来る!」という根拠の無い万能感を抱き休暇へ突入するも、 大して何もできずに終わるというGW恒例現象へのささやかなる抵抗として、まずはfzfによるCUI生活の改善を。


fzfとripgrepのインストール

fzfをインスト-ル。

brew install fzf

ripgrepをインストール。

brew install ripgrep

fzfはパイプ経由で候補リストが渡されなかった場合、カレントディレクトリ配下のファイル一覧が候補リストとなる。
ファイル一覧の作成にはfindコマンドが使用されるが環境変数 FZF_DEFAULT_COMMAND を設定することで任意のfind代替コマンドを利用することができる。

今回は$XDG_CONFIG_HOME/fish/conf.d/fzf.fish を作成、以下のように設定し、

set -x FZF_DEFAULT_COMMAND 'rg --files --hidden --follow --glob "!.git/*"'
  • ripgrepを利用
  • 隠しファイルも対象
  • .git配下を無視
  • gitignoreに定義済みの対象を無視
  • シンボリックリンクを追う

とした。

fzfコマンドを利用する

ファインダーの操作

fzfコマンドを実行すると候補リスト選択、絞り込みのためのファインダーが表示される。
ファインダーの操作方法は以下の通り。

CTRL-C CTRL-G ESC のいずれで終了。

CTRL-P CTRL-K のいずれかで上へ移動。

CTRL-J CTRL-N のいずれかで下へ移動。

またfzf -mで複数選択可能とした場合は、

TAB SHIFT-TAB CTRL-I のいずれかで選択していくことができる。

絞り込み検索

またファインダー上でクエリを入力すると候補リストに対して絞り込み検索が行われる。
クエリとして入力した文字列はあいまい検索となるが、以下の記法によってマッチルールを指定することができる。

部分完全一致
例:'<文字列>
先頭にシングルクォートを付加することで部分完全一致となる。
この文字列が含まれるものが絞り込まれる。
前方完全一致
例:^<文字列>
先頭にハットを付加することで前方完全一致となる。
この文字列から始まるものが絞り込まれる。
後方完全一致
例:<文字列>$
末尾にドルを付加することで後方完全一致となる。
この文字列で終わるものが絞り込まれる。
否定
例:!<文字列>
先頭に感嘆符を付与することで否定となる。
デフォルトでは部分完全一致の否定となるが、前方完全一致、後方完全一致と組み合わせて使用することもできる。

AND条件
例: '<文字列A> '<文字列B>
複数の条件を空白区切りで指定した場合、AND条件となる。
OR条件
例: '<文字列A> | '<文字列B>
複数の条件をバーティカルバー区切りで指定した場合、OR条件となる。

tmuxとの統合

tmux環境ではfzfに同梱されているfzf-tmuxコマンドを利用することができる。
fzf-tmuxはtmux環境用のfzfコマンドラッパーでオリジナルのfzfの機能に加えて自動的にファインダー用のペインを作成、確定後に自動的に削除するtmux固有の機能が付加されている。

fishから利用する

fishのfzfプラグインfisherman経由でインストール。

fisher fzf

キーバインド

fzfプラグインは新旧二種類のキーバインドがあり、環境変数FZF_LEGACY_KEYBINDINGSに0を指定することで新キーバインドが有効となる。 今回は新キーバインドを利用するため$XDG_CONFIG_HOME/fish/conf.d/fzf.fishに追記する。

set -x FZF_DEFAULT_COMMAND 'rg --files --hidden --follow --glob "!.git/*"'
set -x FZF_LEGACY_KEYBINDINGS 0

また新キーバインドではファイル検索が CTRL-F となっており、自環境ではfishのオートサジェスチョンの受け入れキーマッピングと衝突するため、 サジェスチョンの受け入れキーを CTRL-E へ移動した。

$XDG_CONFIG_HOME/fish/functions/fish_user_key_bindings.fish

function fish_user_key_bindings

    for mode in insert default visual
        fish_default_key_bindings -M $mode
    end
    fish_vi_key_bindings --no-erase

    bind -M insert -e \ce
    bind -M visual -e \ce
    bind -M default -e \ce
    bind -M insert -e \cf
    bind -M insert \ce accept-autosuggestion

    ### fzf ###
    set -q FZF_LEGACY_KEYBINDINGS
    or set -l FZF_LEGACY_KEYBINDINGS 1
    if test "$FZF_LEGACY_KEYBINDINGS" -eq 1
        bind \ct '__fzf_find_file'
        bind \cr '__fzf_reverse_isearch'
        bind \cx '__fzf_find_and_execute'
        bind \ec '__fzf_cd'
        bind \eC '__fzf_cd_with_hidden'
        if bind -M insert >/dev/null ^/dev/null
            bind -M insert \ct '__fzf_find_file'
            bind -M insert \cr '__fzf_reverse_isearch'
            bind -M insert \cx '__fzf_find_and_execute'
            bind -M insert \ec '__fzf_cd'
            bind -M insert \eC '__fzf_cd_with_hidden'
        end
    else
        bind \cf '__fzf_find_file'
        bind \cr '__fzf_reverse_isearch'
        bind \ex '__fzf_find_and_execute'
        bind \ed '__fzf_cd'
        bind \eD '__fzf_cd_with_hidden'
        if bind -M insert >/dev/null ^/dev/null
            bind -M insert \cf '__fzf_find_file'
            bind -M insert \cr '__fzf_reverse_isearch'
            bind -M insert \ex '__fzf_find_and_execute'
            bind -M insert \ed '__fzf_cd'
            bind -M insert \eD '__fzf_cd_with_hidden'
        end
    end
    ### fzf ###
end

ripgrepを利用する

先ほどの環境変数FZF_DEFAULT_COMMANDの定義によりパイプなしでfzfコマンドを実行した場合ripgrepによるファイル検索が行われるようになっているが、 fishのfzfプラグイン経由でfzfコマンドを実行した場合は常にパイプ経由での利用となり、ファイル検索時はfzfプラグインが自前でfindコマンドを発行し結果をパイプで渡しているため ripgrepが利用されない。

このためFZF_DEFAULT_COMMANDとは別にfishのfzfプラグインが利用するfind代替コマンドを指定するための環境変数FZF_FIND_FILE_COMMANDを定義し fzfプラグインからもripgrepが使われるようにする。
$XDG_CONFIG_HOME/fish/conf.d/fzf.fish に追記。

set -x FZF_DEFAULT_COMMAND 'rg --files --hidden --follow --glob "!.git/*"'
set -x FZF_LEGACY_KEYBINDINGS 0
set -x FZF_FIND_FILE_COMMAND $FZF_DEFAULT_COMMAND

入力中のコマンドラインにファイル名を展開する

fishのコマンドライン入力中に CTRL-F でカレントディレクトリ配下のファイル一覧がファインダー表示。
確定後、入力中のコマンドラインのカーソル位置にファイル名が展開される。

コマンド履歴の呼び出し

fishのコマンドライン入力中に CTRL-R でコマンド履歴がファインダー表示。
既にコマンドラインに入力中の文字列がある場合は、その文字列がクエリとなる。

vimから利用する

vim用fzfプラグインfzf.vimを利用する。
今回はneovim & vim-plug の構成で利用。

プラグイン追加

今回fzf本体はHomebrew経由でインストールしているため、init.vimへのプラグイン定義は以下のようにする。

Plug '/usr/local/opt/fzf'
Plug 'junegunn/fzf.vim'

fzf用キーマップの追加

多用しそうな機能へのキーマップ定義をinit.vimへ追加する。

let g:mapleader=' '

...

nnoremap [Fzf] <Nop>
nmap <Leader>f [Fzf]
nnoremap [Fzf]f :<C-u>Files<CR>
nnoremap [Fzf]g :<C-u>GFiles<CR>
nnoremap [Fzf]G :<C-u>GFiles?<CR>
nnoremap [Fzf]b :<C-u>Buffers<CR>

vimからファイルを開く

Leader f f でカンレントディレクトリ配下のファイル一覧が候補となる。
絞り込み方はfzf単体で利用する場合とまったく同じだが絞り込み後のアクションをいくつか選択できる。

タブで開く
CTRL-T でタブを開く。 単純に ENTER の場合も同じ。
水平画面分割で開く
CTRL-X で水平画面分割で開く。
垂直画面分割で開く
CTRL-V で垂直画面分割で開く。

vimからgit管理のファイルを開く

Leader f g でgitリポジトリ内のファイル一覧が候補となる。

vimからgit管理の変更のあったファイルを開く

Leader f G でgitリポジトリ内の変更のあったファイル一覧が候補となる。
ファインダーでは一覧の他にファイルの差分情報がプレビューされる。

vimからバッファを選択する

Leader f b でvimのバッファ一覧が候補となる。