2018年10月8日月曜日

bashで編集可能な補完を実現する方法

fzfを使った時にCtrl-Tなどのキーバインドで「編集可能な補完」を実現している方法がわからなかったので調べてみました。 ~/.fzf/shell/key-bindings.bash を読むと重要なのは以下。

fzf-file-widget() {
  if __fzf_use_tmux__; then
    __fzf_select_tmux__
  else
    local selected="$(__fzf_select__)"
    READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
    READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
  fi
}
bind -x '"\C-t": "fzf-file-widget"'

bindコマンドでCtrl-Tでfzf-file-widget関数を呼び出しているけど、 内部では__fzf_select__、ようするにfzfを起動して選択パスをselectedに保存している。 その後READLINE_LINEとREADLINE_POINTを設定する事で、編集可能な文字列の入力を実現しているようです。

このREADLINE_LINEとREADLINE_POINTの仕組みは覚えておくと結構便利そうですね。 しかしbashの公式リファレンス見てもサラッとしか書いてない。 おそらくここが最も詳しく説明しているページなので、そちらを読んだほうが良いけど、軽くまとめると以下のようになっている。
# 出力文字列の設定
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
       # 先頭からカーソル位置までの現在の文字列 | 選択文字 | カーソル位置より後ろの現在の文字列

# カーソル位置の設定
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))


ところでこの機能についての補足ですが、fzfではbash3以前では別の方法で実現しており、そちらについては割愛してます。 またzshやfishだとまた違った方法で実現するしかないようで、あまり深みにハマらないほうが良いかも知れません。 シェルスクリプトの種類ごとに実装が異なるという事は、有名ではないシェルだと編集可能な補完がそもそもできない可能性もあるかも知れない。


応用してみます。
コマンドを補完してアプリを選択し、引数を付けて実行しやすくしてみましょう。
__launch() {
    local selected="$(compgen -ac | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_CTRL_T_OPTS" fzf) "
    READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
    READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
}
bind -x '"\C-j": "__launch"'

上記を~/.bashrcなどに記述すると、fzfを利用してCtrl-Jで実行ファイルの入力補完ができ、補完後に実行オプションを再編集できます。

0 件のコメント: