From 5e75e4044f7f5a70d743af6b47009354ab518c4e Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Thu, 6 Dec 2018 11:26:14 +1300 Subject: 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$ --- bash/bash_completion.d/sd.bash | 47 ++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-) (limited to 'bash/bash_completion.d/sd.bash') diff --git a/bash/bash_completion.d/sd.bash b/bash/bash_completion.d/sd.bash index 4dc72f31..8adc9810 100644 --- a/bash/bash_completion.d/sd.bash +++ b/bash/bash_completion.d/sd.bash @@ -14,25 +14,46 @@ _sd() { # Make globs expand appropriately shopt -s dotglob nullglob + + # Get list of siblings; use trailing slashes to limit to directories + # There should always be at least one (self) + siblings=(../*/) + + # Strip leading dot-dot-slash and trailing slash + siblings=("${siblings[@]#../}") + siblings=("${siblings[@]%/}") + + # Add quoted siblings to new array; for large directories, this is + # faster than forking a subshell for `printf %q` on each item + while read -d / -r sibling ; do + siblings_quoted[sqi++]=$sibling + done < <(printf '%q/' "${siblings[@]}") + + # Make matching work appropriately if _completion_ignore_case ; then shopt -s nocasematch 2>/dev/null fi - # Print matching sibling dirs that are not the current dir - for sib in ../*/ ; do - # Strip leading ../ - sib=${sib#../} - # Strip trailing slash - sib=${sib%/} - # Skip self - [[ $sib != "${PWD##*/}" ]] || continue - # Check the quoted and unquoted word for matching - for match in "$sib" "$(printf '%q' "$sib")" ; do - # Print any match, slash-terminated + # Get current dir + self=${PWD##*/} + + # Iterate through keys of the siblings array + for si in "${!siblings[@]}" ; do + + # Get sibling and associated quoted sibling + sibling=${siblings[si]} + sibling_quoted=${siblings_quoted[si]} + + # Skip if this sibling looks like the current dir + [[ $sibling != "$self" ]] || continue + + # If either the unquoted or quoted sibling matches, print the + # unquoted one as a completion reply + for match in "$sibling" "$sibling_quoted" ; do case $match in ("$2"*) - printf '%s/' "$sib" - continue + printf '%s/' "$sibling" + break ;; esac done -- cgit v1.2.3