zshでhistory-beginning-search-backwardをCtrl-Pなどに割り当て、入力したところまでマッチするように履歴から補完するように設定している方は多いと思います。

しかし、プロンプト先頭からカーソル位置までが検索対象となってしまうため、先にsudoを入れたり、パイプの途中から補完するといったことはできません。

そこで、現在のプロンプト入力からいくつか指定のパターンを除外した上で補完できるようなzleのウィジェットを書いてみました。

履歴に

sudo apt-get update
make
make install
ldconfig
less

があったとして、普通は

sudo m(補完)

としても何も補完されませんが、この設定を用いると

sudo make install => sudo make

のように補完してくれます。

sudo (補完)

(補完に必要な文字列を何も与えない)としたとき、今回の機能を有効にするかどうかは好みが分かれるところだと思いますが、一応安全な方を取って無効にし、普通にsudoで始まる履歴を検索して

sudo apt-get update

と補完するようにしました。

キーバインドは過去方向がC-P、逆がC-Nです。

# complete from history ignoring leading (sudo, '|', man, which, ..) in current prompt
# only complete in this way if there are some other input than those ignoring patterns
# examples with history:
#  ldconfig
#  make
#  make install
#  less
# case:
#  $ sudo <C-P>  => $ sudo ldconfig
#  $ sudo m<C-P>  => $ sudo make install => $ sudo make
#  $ wget -O - http://.../ | l<C-P> => $ wget -O - http://.../ | less
SMART_SEARCH_HISTORY_PATTERN='(sudo|\||man|which)'
function smart-search-history {
  local trim="$(echo "$LBUFFER" | sed -r "s/^.*${SMART_SEARCH_HISTORY_PATTERN} *//")"
  local old_leader="$(echo "$LBUFFER" | sed -r "/${SMART_SEARCH_HISTORY_PATTERN}/s/(^.*${SMART_SEARCH_HISTORY_PATTERN} *).+?$/\\1/p;d")"
  if [ -n "$trim" ]; then
    LBUFFER="$trim"
    zle $1
    LBUFFER="$old_leader""$LBUFFER"
  else
    zle $1
  fi
}
function smart-search-history-backward {
  smart-search-history history-beginning-search-backward
}
function smart-search-history-forward {
  smart-search-history history-beginning-search-forward
}

zle -N smart-search-history-backward
bindkey '^P' smart-search-history-backward
zle -N smart-search-history-forward
bindkey '^N' smart-search-history-forward

前回、空のプロンプトでEnterを入力するとlsするウィジェットを書いたときに$BUFFERの便利さに気づきましたが、今回もその流れで$LBUFFERというzleの変数を用いています。