diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2015-10-21 13:08:09 +1300 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2015-10-21 13:08:09 +1300 |
commit | dcfc80aaa112498498d93bae3ae0eb2088244c47 (patch) | |
tree | 889854ba1c96f1c86465da1bbd5cc87f0c8d4b9f /bash/bashrc.d/bd.bash | |
parent | Handle spaces correctly in completions (diff) | |
download | dotfiles-dcfc80aaa112498498d93bae3ae0eb2088244c47.tar.gz dotfiles-dcfc80aaa112498498d93bae3ae0eb2088244c47.zip |
Tidy up completion considerably; no more compgen
* Remove all instances of compgen; for filename completion it's quite
broken as it relies on implicit wordsplitting in array context, and
doesn't have an option to print with a null delimiter; replaced with
manual for/while loops instead
* Add IFS= to while/read loops over filenames
* Use "dirname/s" instead of "dir/s" variables to avoid keyword
collisions and for clarity
* Remove some unnecessary variables
* Use shorter syntax for loop exit conditions
* Move completion options into functions where applicable rather than
having them on the completion definition itself
Diffstat (limited to 'bash/bashrc.d/bd.bash')
-rw-r--r-- | bash/bashrc.d/bd.bash | 56 |
1 files changed, 31 insertions, 25 deletions
diff --git a/bash/bashrc.d/bd.bash b/bash/bashrc.d/bd.bash index 110fca06..e4a6738e 100644 --- a/bash/bashrc.d/bd.bash +++ b/bash/bashrc.d/bd.bash @@ -34,24 +34,24 @@ bd() { [[ $req != / ]] || req=${req%/} # What to do now depends on the request - local dir + local dirname case $req in # If no argument at all, just go up one level '') - dir=.. + dirname=.. ;; # Just go straight to the root or dot directories if asked /|.|..) - dir=$req + dirname=$req ;; # Anything else with a leading / needs to anchor to the start of the # path /*) - dir=$req - if [[ $PWD != "$dir"/* ]] ; then + dirname=$req + if [[ $PWD != "$dirname"/* ]] ; then printf 'bash: %s: Directory name not in path\n' \ "$FUNCNAME" >&2 return 1 @@ -59,13 +59,13 @@ bd() { ;; # In all other cases, iterate through the directory tree to find a - # match, or whittle the dir down to an empty string trying + # match, or whittle the dirname down to an empty string trying *) - dir=${PWD%/*} - while [[ -n $dir && $dir != */"$req" ]] ; do - dir=${dir%/*} + dirname=${PWD%/*} + while [[ -n $dirname && $dirname != */"$req" ]] ; do + dirname=${dirname%/*} done - if [[ -z $dir ]] ; then + if [[ -z $dirname ]] ; then printf 'bash: %s: Directory name not in path\n' \ "$FUNCNAME" >&2 return 1 @@ -74,26 +74,32 @@ bd() { esac # Try to change into the determined directory - builtin cd "${opts[@]}" -- "$dir" + builtin cd "${opts[@]}" -- "$dirname" } # Completion setup for bd _bd() { - local word - word=${COMP_WORDS[COMP_CWORD]} - - # Build a list of dirs in $PWD - local -a dirs - while read -d / -r dir ; do - if [[ -n $dir ]] ; then - dirs=("${dirs[@]}" "$dir") - fi - done < <(printf %s "$PWD") - - # Complete with matching dirs + + # The completions given are filenames and may require escaping compopt -o filenames - local IFS=$'\n' - COMPREPLY=( $(compgen -W "${dirs[*]}" -- "$word") ) + + # Only makes sense for the first argument + ((COMP_CWORD == 1)) || return 1 + + # Build a list of dirnames in $PWD + local -a dirnames + IFS=/ read -d '' -a dirnames < <(printf '%s\0' "${PWD#/}") + + # Remove the last element in the array (the current directory) + ((${#dirnames[@]})) || return 1 + dirnames=("${dirnames[@]:0:$((${#dirnames[@]}-1))}") + + # Add the matching dirnames to the reply + local dirname + for dirname in "${dirnames[@]}" ; do + [[ $dirname == "${COMP_WORDS[COMP_CWORD]}"* ]] || continue + COMPREPLY=("${COMPREPLY[@]}" "$dirname") + done } complete -F _bd bd |