autoload -Uz chpwd_recent_dirs cdr add-zsh-hook add-zsh-hook chpwd chpwd_recent_dirs zstyle ':chpwd:*' recent-dirs-max 50 cdselect() { local dirs=("${(@f)$(cdr -l 2>/dev/null | sed 's/^[0-9]* *//')}") [[ ${#dirs} -eq 0 ]] && return local query="" cursor=1 selected="" while true; do local filtered=() for d in "${dirs[@]}"; do local match=1 for word in ${(s: :)query}; do [[ "${d:l}" != *"${word:l}"* ]] && match=0 && break done [[ $match -eq 1 ]] && filtered+=("$d") done # sort by path length (shortest first) local sorted=() for f in "${filtered[@]}"; do sorted+=("${#f} $f"); done sorted=(${(n)sorted}) filtered=() for s in "${sorted[@]}"; do filtered+=("${s#* }"); done [[ $cursor -gt ${#filtered} ]] && cursor=${#filtered} [[ $cursor -lt 1 ]] && cursor=1 clear echo "cd> $query" local i=1 for d in "${filtered[@]}"; do if [[ $i -eq $cursor ]]; then printf "\e[7m %s\e[0m\n" "$d" else printf " %s\n" "$d" fi ((i++)) [[ $i -gt 20 ]] && break done read -k1 key 2>/dev/null if [[ "$key" == $'\x1b' ]]; then read -k1 -t 0.1 k2 2>/dev/null if [[ "$k2" == "[" ]]; then read -k1 -t 0.1 k3 2>/dev/null case "$k3" in A) ((cursor--)) ;; # up B) ((cursor++)) ;; # down esac continue else break # plain ESC fi fi case "$key" in $'\n'|$'\r') [[ ${#filtered} -gt 0 ]] && selected="${filtered[$cursor]}"; break ;; $'\x0e') ((cursor++)) ;; # C-n $'\x10') ((cursor--)) ;; # C-p $'\x7f'|$'\b') query="${query%?}"; cursor=1 ;; *) query+="$key"; cursor=1 ;; esac done clear if [[ -n "$selected" ]]; then selected="${selected/#\~/$HOME}" selected="${selected//\\ / }" cd "$selected" fi zle reset-prompt } zle -N cdselect bindkey '^j' cdselect