# `cdp` to activate an interactive way to navigate directories function cdp { # helpers for terminal print control, key input, and other things ESC=$( printf "\033" ) cursor_blink_on() { printf "${ESC}[?25h"; } cursor_blink_off() { printf "${ESC}[?25l"; } cursor_to() { printf "${ESC}[$1;${2:-1}H"; } print_option() { printf " $1 $(tput el)"; } print_selected() { printf "${ESC}[7m $1 ${ESC}[27m$(tput el)"; } get_cursor_row() { IFS=';' read -sdR $'?\E[6n' ROW COL; echo ${ROW#*\[}; } key_input() { read -sk1 'key?' case $key in $'\n') echo enter;; ' ') echo space;; 0) echo 10;; 1|2|3|4|5|6|7|8|9) echo $key;; "${ESC}") read -sk2 key if [[ $key = "[A" ]]; then echo up elif [[ $key = "[B" ]]; then echo down elif [[ $key = "[C" ]]; then echo right elif [[ $key = "[D" ]]; then echo left fi;; esac } min() { printf "%s\n" "$@" | sort -g | head -n1 } max() { printf "%s\n" "$@" | sort -gr | head -n1 } # initialize persistent variables MAX_SIZE=10 local prev_dir='' local orig_dir=$PWD while true; do # determine the options local subdirs=$((ls -1d */) 2> /dev/null) local opts=("$(tput smul)Change directory to $(tput bold)$(pwd)$(tput sgr0)$(tput el)") [[ ! -z $subdirs ]] && opts+=("${(f)subdirs}") opts+=('../') local num_opts=${#opts[@]} # initialize interaction area and screen positions of first and last row (used for overwriting) # note: indices are all 1-indexed with row 1 being the line displaying the working directory local num_opt_rows=$(min $(tput lines) $MAX_SIZE $((num_opts - 1))) for _ in {1..$num_opt_rows}; do echo; done local last_row=$(get_cursor_row) local first_row=$((last_row - num_opt_rows)) cursor_to $first_row echo " $opts[1]" # ensure cursor and input echoing back on upon a ctrl+c during read trap "cd $orig_dir; cursor_blink_on; stty echo; printf '\n'; return" 2 cursor_blink_off # jump to previous dir option (if left arrow key was pressed) local selected=2 for i in {2..$#opts}; do [[ $opts[$i] = $prev_dir ]] && selected=$i; done local inp='' local top_row=$(max $((selected - num_opt_rows + 2)) 2) while true; do # determine "scroll" position if [[ $selected -lt $top_row ]]; then top_row=$selected elif [[ $selected -ge $((top_row + num_opt_rows)) ]]; then top_row=$((selected - num_opt_rows + 1)); fi # print options by overwriting the last lines for opt_index in {$top_row..$((top_row + num_opt_rows - 1))}; do cursor_to $((first_row + opt_index - top_row + 1)) printf " [%i] " $(((opt_index - top_row + 1) % 10)) if [[ $opt_index -eq $selected ]]; then print_selected $opts[$((opt_index))] else print_option $opts[$((opt_index))]; fi done # user key control inp=$(key_input) case $inp in left) [[ $PWD != $HOME ]] && break;; up) ((selected--)); [[ $selected -lt 2 ]] && selected=$num_opts;; down) ((selected++)); [[ $selected -gt $num_opts ]] && selected=2;; right|enter|space|1|2|3|4|5|6|7|8|9|10) break;; esac done # reset interaction area cursor_to $first_row for row_offset in {1..$num_opt_rows}; do cursor_to $((first_row + row_offset)); echo -n $(tput el); done cursor_to $first_row # cd accordingly case $inp in right) cd $opts[$selected]; prev_dir='';; enter) break;; space) cd; prev_dir='';; left) prev_dir=$(printf '%s/' "${PWD##*/}"); cd ..;; *) local dest=$opts[$((top_row + inp - 1))] [[ $dest == '../' ]] && prev_dir=$(printf '%s/' "${PWD##*/}") cd $dest;; esac done cursor_blink_on }