aboutsummaryrefslogtreecommitdiff
path: root/bash/bashrc.d/bd.bash
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2015-10-21 13:08:09 +1300
committerTom Ryder <tom@sanctum.geek.nz>2015-10-21 13:08:09 +1300
commitdcfc80aaa112498498d93bae3ae0eb2088244c47 (patch)
tree889854ba1c96f1c86465da1bbd5cc87f0c8d4b9f /bash/bashrc.d/bd.bash
parentHandle spaces correctly in completions (diff)
downloaddotfiles-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.bash56
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