#!/bin/bash # `cdp` to activate an interactive way to navigate directories # note: for consistency, treat all arrays as 0-indexed cdp() { local MAX_VOPTS=8 MAX_DIR_WIDTH=60 SEARCH_ICON="⌕" local SEARCH_PREFIX=" $(tput bold)${SEARCH_ICON}$(tput sgr0)$(tput el) " local is_bash=$([[ -n $BASH ]] && echo true || echo false) local is_zsh=$([[ -n $ZSH_NAME ]] && echo true || echo false) # helper functions translate_input() { # args: input case $1 in $'\n'|'') echo enter;; $'\177'|$'\b') echo backspace;; "[A") echo up;; "[B") echo down;; "[C") echo right;; "[D") echo left;; *) echo "$1";; esac } zsh_key_input() { read -sk1 key [[ $key = $'\e' ]] && read -sk2 -t 0.1 key translate_input "$key" } bash_key_input() { IFS='' read -rsn1 key [[ $key = $'\e' ]] && read -rsn2 -t 1 key translate_input "$key" } index_array() { # args: index, array local i=$1 shift 1 echo "${@:$((i+1)):1}" # only way for array indexing to work for both bash and zsh # ${@:0:1} will return the function name } index_of() { # args: element, array local e=$1 shift 1 local i=0 for s in "$@"; do if [[ $s = "$e" ]]; then echo $i return fi ((i++)) done echo -1 } cursor_to_first_option() { tput rc; tput cud1; tput cud1 } print_option() { echo " $1 $(tput el)" } print_selected() { echo " $(tput setab 7)$(tput setaf 0) $1 $(tput sgr0)$(tput el)" } render_options() { # precondition: cursor is at the first line of the options # args: selected index (of visible options), all visible options (as array) local selected=$1 shift 1 local options=("$@") local i=0 for s in "${options[@]}"; do if [[ $i -eq $selected ]]; then print_selected "$s"; else print_option "$s"; fi ((i++)) done } render_search_string() { # args: search string # postcondition: cursor is at the first line of the options tput rc; tput cud1; tput cuf 4 echo "$(tput setaf 3)${search_str}$(tput el)$(tput sgr0)_" } render_heading() { # args: none tput rc local pwd_str=$(pwd) local lim_width=$(($(tput cols) - 20 - 5)) # 20 for "Change directory to ", 5 for buffer [[ lim_width -gt MAX_DIR_WIDTH ]] && lim_width=$MAX_DIR_WIDTH [[ ${#pwd_str} -gt $lim_width ]] && pwd_str="...${pwd_str:$((${#pwd_str} - lim_width + 3))}" echo "$(tput smul)Change directory to $(tput bold)${pwd_str}$(tput sgr0)$(tput el)" } # initialize variables local search_str='' prev_dir='' orig_dir=$PWD local num_vopts=$(($(tput lines) - 2 - 1)) # 2 fixed lines, 1 for buffer [[ $num_vopts -gt $MAX_VOPTS ]] && num_vopts=$MAX_VOPTS local regex_chars='$^.?+*(){}[]/' escape_regex() { # args: string local str=$1 c='' for ((i=0; i<${#regex_chars}; i++)); do c=${regex_chars:$i:1} str=${str//"$c"/\\$c} done echo "$str" } # initialize interface tput civis stty -echo tpuc sc && render_heading echo -e "$SEARCH_PREFIX" for ((i=0; i