【fzf】ターミナル上でファイル名検索と全文検索をPreview付でやりたい


エディタ・IDE等によくある検索機能をfzfでつくってみよう記事

ファイル名検索は fzfで用意されているKeyBind(Ctrl+T)をカスタマイズします
$(brew --prefix)/opt/fzf/install したときに自動で設定してくれるあれです
widget関数の詳細は /usr/local/opt/fzf/shell/key-bindings.zsh で確認できます

注書きみたいなの

  • 記事内で外部コマンド( bat,rg, fd)使ってます
  • macOS, zshでの動作しか確認してないです

junegunn/fzf: A command-line fuzzy finder
man page

その前に

私のfzf default optionです。
--height は指定ありのほうが見やすくて好きです。

.zshrc
export FZF_DEFAULT_OPTS="
    --height 90% --reverse --border
    --prompt='➜  ' --margin=0,1 --inline-info
    --tiebreak=index --no-mouse --filepath-word
    --color fg:-1,bg:-1,hl:33,fg+:250,bg+:235,hl+:33
    --color info:37,prompt:37,pointer:230,marker:230,spinner:37
    --bind='ctrl-w:backward-kill-word,ctrl-x:jump,down:preview-page-down'
    --bind='ctrl-z:ignore,ctrl-]:replace-query,up:preview-page-up'
    --bind='ctrl-a:toggle-all,?:toggle-preview'
"

bindkeyの設定については こちら

改行ありTextが個人的に嫌だったので実際には配列、連想配列を使って記述してます
つまりこう↓

.zshrc
# fzf
export FZF_COMPLETION_TRIGGER=','
typeset -Tgx FZF_DEFAULT_OPTS fzf_default_opts " " 
fzf_default_opts=(
  '--height=90%'
  '--reverse'
  '--border'
  '--inline-info'
  '--prompt="➜  "'
  '--margin=0,2'
  '--tiebreak=index'
  '--no-mouse'
  '--filepath-word'
)

() {
  local -A color_map=(
    [fg]='-1'
    [bg]='-1'
    [hl]='33'
    [fg+]='250'
    [bg+]='235'
    [hl+]='33'
    [info]='37'
    [prompt]='37'
    [pointer]='230'
    [marker]='230'
    [spinner]='37'
  )

  for x in "${(k)color_map[@]}"; do
    fzf_color_opts+=("${x}:${color_map[${x}]}") 
  done
  fzf_default_opts+=( '--color="'"${(j.,.)fzf_color_opts}"'"' )
}

() {
  local -a fzf_bind_opts=()
  local -A bind_map=(
    [?]='toggle-preview'
    [ctrl-a]='toggle-all'
    ['ctrl-]']='replace-query'
    [ctrl-w]='backward-kill-word'
    [ctrl-x]='jump'
    [ctrl-z]='ignore'
    [up]='preview-page-up'
    [down]='preview-page-down'
  )

  for x in "${(k)bind_map[@]}"; do
    fzf_bind_opts+=("${x}:${bind_map[${x}]}")
  done
  fzf_default_opts+=( '--bind="'"${(j:,:)fzf_bind_opts}"'"' )
}

連想配列の記述がversion5.5以上のzshじゃないと読み込めないです
普通に平文でもおk

見た目はこんな感じ

ファイル名検索

デフォルトの動きは「カレントディレクトリ配下のファイル・ディレクトリを検索」です

今回は以下のようにカスタマイズしたいと思います

  • デフォルトコマンドをfdに変更。検索対象はファイルのみに
  • 選択なかったら自動終了、1件のみの場合は自動で選択
  • プレビューの表示 (batコマンド使う)
  • 開く (できれば別ペインで)
  • 隠しファイルの表示切替

記述がこちら

.zshrc
export FZF_CTRL_T_COMMAND="fd --type f "
export FZF_CTRL_T_OPTS="
    --select-1 --exit-0
    --bind 'ctrl-l:execute(tmux splitw -h -- nvim {})'
    --bind '>:reload($FZF_ALT_C_COMMAND -H -E .git )'
    --bind '<:reload($FZF_ALT_C_COMMAND)'
    --preview 'bat -r :100 --color=always --style=header,grid {}'"

こんな具合になります

全文検索

カレントディレクトリ配下のファイルを全文検索。選択したファイルをコマンドラインに貼り付ける

manにある記述を参考にしてます
queryを変更するたび rg で検索が走ります

作ったもの

fzf-ripgrep-widget
#!/usr/bin/env zsh

main() {
  LBUFFER="${LBUFFER}$(__fzf_ripgrep)"
  local ret=$?
  zle reset-prompt
  return $ret
}

__fzf_ripgrep() {
  emulate -L zsh
  rg_cmd="rg --smart-case --line-number --color=always --trim" 
  selected=$(FZF_DEFAULT_COMMAND=":" \
      fzf --bind="change:top+reload($rg_cmd {q} || true)" \
          --bind="ctrl-l:execute(tmux splitw -h -- nvim +/{q} {1} +{2})" \
          --ansi --phony \
          --delimiter=":" \
          --preview="bat -H {2} --color=always --style=header,grid {1}" \
          --preview-window='down:60%:+{2}-10')

  local ret=$?
  [[ -n "$selected" ]] && echo ${${(@s/:/)selected}[1]}
  return $ret
}

main 
.zshrc
autoload -Uz fzf-ripgrep-widget

zle -N fzf-ripgrep-widget
bindkey '\ef' fzf-ripgrep-widget

動き

検索結果がpreview内で追えて満足
ただハイライトが少し微妙なのと、 検索結果windowのformatが残念

感想

いくつか課題が残ってますが、これが現状MAX
Rust関連のコマンドに触れてRustすげーとなっております