# # Expand dots to dot-dot component (~/.bash_aliases). # # https://github.com/bac0n/bash_completion # if [ -n "$PS1" ]; then cd(){ local d=${@:(-1)} local x='' y='' z=3 while [[ ! -d $d ]] && [[ ${d:${#x}} =~ (^|/)([.]{3,})(/.*)?$ ]]; do x=${d:0:(${#d} - ${#BASH_REMATCH[3]})} if [[ ! -d $x ]]; then y='..' for ((z=3; z <= ${#BASH_REMATCH[2]}; z++)); do y+='/..' done d=${d:0:(${#d} - ${#BASH_REMATCH[0]})}${BASH_REMATCH[1]}${y}${BASH_REMATCH[3]} fi done builtin pushd "$d" > /dev/null && /bin/ls --almost-all --color=auto } cd_undo(){ if [[ ${READLINE_LINE%% *} != cd ]]; then return fi local d='' x='' y='' z=0 READLINE_LINE=${READLINE_LINE%/} d=$(rev <<< ${READLINE_LINE#* }) if [[ $d =~ (^|/)([.]{2}(/[.]{2})+)(/.*)?$ ]]; then y=. x=${BASH_REMATCH[2]//\/} for ((z=0; z < (${#x} / 2); z++)); do y+=. done x=${d:0:(${#d} - ${#BASH_REMATCH[0]})} READLINE_LINE="cd $(rev <<< ${x}${BASH_REMATCH[1]}${y}${BASH_REMATCH[4]})" READLINE_POINT=$((${#READLINE_LINE} - ${#x} - ${#BASH_REMATCH[1]})) else i=0 x=$((${#READLINE_LINE} - $READLINE_POINT)) while [[ ${d:$x} =~ (^|/)([.]{3,})(/.*)?$ ]]; do x=$((${#d} - ${#BASH_REMATCH[3]})) if (( (${#BASH_REMATCH[2]} + ${#BASH_REMATCH[3]}) == ($READLINE_POINT - 3) )); then continue fi READLINE_POINT=$((${#BASH_REMATCH[0]} - ${#BASH_REMATCH[1]} + 3)) break done fi } # bind cd undo to readline keyseq ESC ESC. if [[ $(declare -F cd_undo) = cd_undo ]]; then bind -r '"\e\e"' bind -x '"\e\e" :cd_undo' bind -x '"\e\e\e":cd_undo' fi fi ## # (/etc/bash_completion.d/cd) # # This meta-cd function observes the CDPATH variable, so that cd additionally # completes on directories under those specified in CDPATH. # _cd() { local cur prev words cword _init_completion || return local IFS=$'\n' i j k compopt -o filenames # Expand dots to dot-dot component. local x='' y='' z=3 while [[ ! -d $cur ]] && [[ ${cur:${#x}} =~ (^|/)([.]{3,})(/.*)?$ ]]; do if [[ -n $(compgen -d -- $cur) ]]; then if [[ $cur == ..+(.) ]] || [[ $cur == */..+(.) ]]; then COMPREPLY+=("$cur/") fi break fi x="${cur:0:(${#cur}-${#BASH_REMATCH[3]})}" if [[ ! -d $x ]]; then y='..' for ((z=3; z <= ${#BASH_REMATCH[2]}; z++)); do y+='/..' done cur="${cur:0:(${#cur}-${#BASH_REMATCH[0]})}${BASH_REMATCH[1]}${y}${BASH_REMATCH[3]}" fi done # Use standard dir completion if no CDPATH or parameter starts with /, # ./ or ../ if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then _filedir -d return fi local -r mark_dirs=$(_rl_enabled mark-directories && echo y) local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y) # we have a CDPATH, so loop on its contents for i in ${CDPATH//:/$'\n'}; do # create an array of matched subdirs k="${#COMPREPLY[@]}" for j in $( compgen -d -- $i/$cur ); do if [[ ( $mark_symdirs && -h $j || $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then j+="/" fi COMPREPLY[k++]=${j#$i/} done done _filedir -d if [[ ${#COMPREPLY[@]} -eq 1 ]]; then i=${COMPREPLY[0]} if [[ "$i" == "$cur" && $i != "*/" ]]; then COMPREPLY[0]="${i}/" fi fi return } if shopt -q cdable_vars; then complete -v -F _cd -o nospace cd pushd else complete -F _cd -o nospace cd pushd fi