🍳

fzfのpreview関連のオプション調べてみた

2022/08/29に公開

どうも。えーたんです。

fzfのオプションについて調べたことを、備忘録として残しておきます。
プレビューウィンドウの話が中心です。

fzfでの波括弧{}の意味

{}

{}はfzfの現在カーソルがある行の文字列です。

find . -name node_modules -prune -name .git -prune -o -type f -print \
  | fzf --preview 'echo {}'

以下の画像の例では./.gitignoreの行にカーソルがあるため、previewのコマンドecho {}echo ./.gitignoreに置き換わって実行されます。

画像

{q}

{q}はfzfのプロンプトに入力している文字列です。

find . -name node_modules -prune -name .git -prune -o -type f -print \
  | fzf --preview 'echo {q}'

以下の画像の例ではfzfのプロンプトにjsを入力しているため、previewのコマンドecho {q}echo jsに置き換わって実行されます。

画像

{数字}

{n}のように数字を指定すると、「現在カーソルがある行の文字列」をスペースで区切ったときのインデックスnの文字列を切り出せます。このインデックスは1始まりです。

例を見てみましょう。

ls -l --time-style=+'%Y-%m-%d' \
  | grep -v / \
  | fzf --preview="echo user={3} when={-2}; cat {-1}" --header-lines=1

画像

まず、最初の2行(lsとgrep)でファイルの一覧を取得しています。この時点での結果は以下のような形式です。

total 108
-rw-r--r--  1 eetann eetann   364 2022-03-21 index.html
-rw-r--r--  1 eetann eetann   366 2022-03-21 package.json
-rw-r--r--  1 eetann eetann 86768 2022-03-21 package-lock.json
-rw-r--r--  1 eetann eetann   162 2022-03-21 vite.config.js

最初の1行目は絞り込みには関係ないため、fzfのオプションで--header-lines=1を指定してヘッダーとして扱います。
lsをgpepした結果を空白区切りとして扱い、3番目である所有者情報を{3}、後ろから2番目である最終更新日を{-2}、最後のファイル名を{-1}のように指定しました。

区切りを変更したい場合、--delimiter :のように--delimiterオプションで指定します。

{+}{+数字}

{}{数字}は「現在カーソルがある行」でしたが、{+}{+数字}のように+をつけると「現在選択している行」の文字列になります。

以下のような--multiオプションで複数選択をするときに使います。

ls -l | grep -v / \
  | fzf --multi \
        --preview="cat {+-1}" \
        --header-lines=1

以下の画像の例では、現在選択している2つのファイル(index.htmlvite.config.js)が表示されています。

画像

--preview-windowオプション

manにはどう書かれているか

man fzfに書かれている--preview-windowの概要は以下です。(長い)。

--preview-window=[POSITION][,SIZE[%]][,border-BORDER_OPT][,[no]wrap][,[no]follow][,[no]cycle][,[no]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]

この中から一部を紹介します。

[,border-BORDER_OPT]

border-BORDER_OPTでは、プレビューウィンドウを区切るための線のスタイルを指定します。

以下はデフォルトのborder-roundedの例です。

ls -l | grep -v / \
  | fzf \
    --preview="echo user={3}; cat {-1}" \
    --header-lines=1 \
    --preview-window=down

画像

次にborder-topの例です。

ls -l | grep -v / \
  | fzf \
    --preview="echo user={3}; cat {-1}" \
    --header-lines=1 \
    --preview-window=down,border-top

画像

[,+SCROLL[OFFSETS][/DENOM]]

[,+SCROLL[OFFSETS][/DENOM]]では、プレビューウィンドウのスクロールの位置を設定します。

+SCROLL

+SCROLLには、プレビューウィンドウのスクロール開始位置を指定します。
以下は+5+SCROLLの部分)を指定した例です。

ls | grep -v / \
  | fzf \
    --preview 'cat {}' \
    --preview-window=down,+5

プレビューウィンドウは5行目から表示されます。

画像

[OFFSETS]

以下は batコマンドを使って+{2}+3+SCROLL[OFFSETS]の部分)を指定した例です。
少し区切って説明していきます。

rg --line-number --no-heading -g '!{.git,node_modules}' --invert-match '^\s*$' \
  | fzf \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window=down,+{2}+3

画像

まず、rgによる出力は以下のような形式です。

src/App.jsx:16:    </div>
src/routes/invoices.jsx:66:    </div>
index.html:10:    <div id="root"></div>
src/App.jsx:4:    <div>
src/routes/invoices.jsx:20:    <div style={{ display: "flex" }}>

「ファイル名」「行番号」「該当箇所」が:で区切られています。
「行番号」を前述の{数字}の形式で切り出してプレビューで扱うため、--delimiterオプションで区切りを:に変更します。
これで{2}と書くと行番号に置き換わります。

今回は「行番号」をプレビューウィンドウのスクロール開始位置とするため、--preview-window+{2}を指定します。+{2}+SCROLL{数字}の組み合わせです。

次に、batコマンドの例です。以下のように最初の3行はヘッダーです。

画像

そのため、+{2}のままだとスクロール開始位置が行番号よりも3行上にずれてしまいます。
このズレは[,+SCROLL[OFFSETS][/DENOM]][OFFSETS]の部分を使うことで修正できます。
今回は3行増えてほしいため、[OFFSETS]として+3を書き加えました。

[/DENOM]

[,+SCROLL[OFFSETS][/DENOM]]/DENOMは、カーソルのある行とマッチするプレビューウィンドウの行の位置を調整してくれます。
具体的には、ウィンドウ上部から1/指定した数字ぐらいの位置にカーソル行が来るように調整してくれます。

rg --line-number --no-heading -g '!{.git,node_modules}' --invert-match '^\s*$' \
  | fzf \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window=+{2}+3/2

以下のように、カーソル行と一致する「プレビューウィンドウの行」が、1/2ぐらいの位置に調整されています。

画像

[,~HEADER_LINES]

[,~HEADER_LINES]を使うことで、プレビューウィンドウにヘッダーを設定できます。

以下の例では、batのヘッダーである1〜3行目をfzfのプレビューウィンドウのヘッダーとして指定しています。

rg --line-number --no-heading -g '!{.git,node_modules}' --invert-match '^\s*$' \
  | fzf \
    --delimiter : \
    --preview 'bat --color=always {1}  --highlight-line {2}' \
    --preview-window=+{2}+3/2,~3

ヘッダーを設定したため、以下の例ではスクロールしてもFile: src/favicon.svgが表示されたままです。

画像

テーマを変える

--colorオプションでfzfのテーマを色をテーマを変えることができます。

以下の例では、fzfの公式ドキュメントに掲載されているNord Themeをもとにした色と、プレビューで使っているbat--theme="Nord"を指定しました。

rg --line-number --no-heading -g '!{.git,node_modules}' --invert-match '^\s*$' \
  | fzf \
    --delimiter : \
    --preview 'bat --color=always {1} --theme="Nord" --highlight-line {2}' \
    --preview-window=+{2}+3/2,~3 \
    --color='bg+:#3B4252,bg:#2E3440,spinner:#81A1C1,hl:#616E88,fg:#D8DEE9,header:#616E88,info:#81A1C1,pointer:#81A1C1,marker:#81A1C1,fg+:#D8DEE9,prompt:#81A1C1,hl+:#81A1C1'

画像

fzfならADVANCED.mdWikibatならコマンドbat --list-themesコードを直接見ると、他のテーマも掲載されています。

筆者の設定

永遠に未完成ですが、2022-08-28現在の筆者の設定を一部掲載します。

以下のように環境変数を設定し、fzfをtmuxのポップアップで表示しています。

export FZF_TMUX=1
export FZF_TMUX_OPTS="-p 80%"

先に画像で表示例をまとめて紹介します。

CTRL + Rの例

画像

プレビューウィンドウを下に配置しています。ワンライナーなどの長いコマンドでは折り返したり、(完全ではないですが)改行ありで実行したコマンドは改行して表示しています。batコマンドで色をつけています。

CTRL + Tの例

画像

ワンライナーなどの長いコマンドでは折り返したり、(完全ではないですが)改行ありで実行したコマンドは改行して表示しています。本記事で登場した[,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]batコマンドを組み合わせています。

ALT + Cの例

画像

ディレクトリ名はあまり長くならないため、プレビューウィンドウを右に配置しています。

コードは以下です。

https://github.com/eetann/dotfiles/blob/92410604f3fea9000a8cf934428886a997429327/zsh/003_plugin.zsh#L17-L75

ZLEとの組み合わせ


ZLEを組み合わせて、入力途中の文字列をいい感じに拾ってfzfのクエリを開始するやつを書いて使ってます。

たとえば、nvim .configの後にCTRL + kを入力すると.configをクエリにしてfzfを開始します。
nvim .config/i3/config zshの後にCTRL + kを入力すると、.config/i3/configはそのまま残し、zshをクエリにしてfzfを開始します。

画像

https://github.com/eetann/dotfiles/blob/92410604f3fea9000a8cf934428886a997429327/zsh/005_myfunction.zsh#L105-L150

参考

Discussion