aboutsummaryrefslogtreecommitdiff
path: root/bash/bash_completion.d/bd.bash
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2018-12-06 11:26:14 +1300
committerTom Ryder <tom@sanctum.geek.nz>2018-12-06 11:30:10 +1300
commit5e75e4044f7f5a70d743af6b47009354ab518c4e (patch)
tree826babc065540c39dcb22fded402a0a96ced3e9c /bash/bash_completion.d/bd.bash
parentMerge branch 'hotfix/v3.0.1' into develop (diff)
downloaddotfiles-5e75e4044f7f5a70d743af6b47009354ab518c4e.tar.gz
dotfiles-5e75e4044f7f5a70d743af6b47009354ab518c4e.zip
Refactor bd()/sd() completion a lot
Avoid very many forks; and work around Bash 3.0 bugs with array behaviour: bash-3.0$ nodes=(a b c) bash-3.0$ printf '%s\n' "${nodes[@]:1}" b c bash-3.0$ nodes=(a b) bash-3.0$ printf '%s\n' "${nodes[@]:1}" bash-3.0 Compare: bash-5.0$ nodes=(a b c) bash-5.0$ printf '%s\n' "${nodes[@]:1}" b c bash-5.0$ nodes=(a b) bash-5.0$ printf '%s\n' "${nodes[@]:1}" b bash-5.0$
Diffstat (limited to 'bash/bash_completion.d/bd.bash')
-rw-r--r--bash/bash_completion.d/bd.bash50
1 files changed, 32 insertions, 18 deletions
diff --git a/bash/bash_completion.d/bd.bash b/bash/bash_completion.d/bd.bash
index 32969522..f6ca3a6b 100644
--- a/bash/bash_completion.d/bd.bash
+++ b/bash/bash_completion.d/bd.bash
@@ -12,38 +12,52 @@ _bd() {
COMPREPLY[ci++]=$comp
done < <(
- # Build an array of path nodes, leaf to root
+ # Build an array of path ancestors
path=$PWD
while [[ -n $path ]] ; do
- node=${path##*/}
+
+ # Peel off the leaf of the path
+ ancestor=${path##*/}
path=${path%/*}
- [[ -n $node ]] || continue
- nodes[ni++]=$node
+
+ # Skip if this is a null string; root, trailing/double slash...
+ [[ -n $ancestor ]] || continue
+
+ # Skip the first non-null element (current dir)
+ ((generation++)) || continue
+
+ # Push node onto ancestry list
+ ancestors[ai++]=$ancestor
done
- # Continue if we have at least two nodes, counting the leaf
- ((ni > 1)) || return
+ # Continue if we have at least one non-root ancestor
+ ((ai)) || return
- # Shift off the leaf, since it is not meaningful to go "back to" the
- # current directory
- nodes=("${nodes[@]:1}")
+ # Add quoted ancestors to new array; for long paths, this is faster than
+ # forking a subshell for `printf %q` on each item
+ while read -d / -r ancestor ; do
+ ancestors_quoted[aqi++]=$ancestor
+ done < <(printf '%q/' "${ancestors[@]}")
# Make matching behave appropriately
if _completion_ignore_case ; then
shopt -s nocasematch 2>/dev/null
fi
- # Iterate through the nodes and print the ones that match the word
- # being completed, with a trailing slash as terminator
- for node in "${nodes[@]}" ; do
- node_quoted=$(printf '%q' "$node")
- # Check the quoted and unquoted word for matching
- for match in "$node" "$(printf '%q' "$node")" ; do
- # Print any match, slash-terminated
+ # Iterate through keys of the ancestors array
+ for ai in "${!ancestors[@]}" ; do
+
+ # Get ancestor and associated quoted ancestor
+ ancestor=${ancestors[ai]}
+ ancestor_quoted=${ancestors_quoted[ai]}
+
+ # If either the unquoted or quoted ancestor matches, print the
+ # unquoted one as a completion reply
+ for match in "$ancestor" "$ancestor_quoted" ; do
case $match in
("$2"*)
- printf '%s/' "$node"
- continue
+ printf '%s/' "$ancestor"
+ break
;;
esac
done