From 80f7c1707c2b25cbb88120f5d12e7e038beba36c Mon Sep 17 00:00:00 2001 From: Andres P Date: Tue, 18 May 2010 17:56:47 -0430 Subject: bash_completion : full rewrite with many improvements * Undeclared local vars with common enough names to warrant breakage * Performance issues with _pacman trying to replicate /usr/bin/pacman with find and other slow tools. * Performance issues with expanding an array (with sometimes hundreds of items) over three times. * Expanding said array to remove already completed entries had the side effect of braking filenames with spaces and or \n. * add -D --database options and --print * fix dirs showing up when they shouldn't in completions * completions regarding database entries shouldn't trigger filename completion. This is now down to 106 lines. The original one (master) is 365 lines long, yet this one retains all functionality. The work is documented in FS#16630. Signed-off-by: Andres P Signed-off-by: Xavier Chantry Signed-off-by: Dan McGee --- contrib/bash_completion | 434 ++++++++++-------------------------------------- 1 file changed, 86 insertions(+), 348 deletions(-) diff --git a/contrib/bash_completion b/contrib/bash_completion index f0b5f9cc..2713ba4d 100644 --- a/contrib/bash_completion +++ b/contrib/bash_completion @@ -1,370 +1,108 @@ -# vim: set ft=sh ts=2 sw=2 et: -# file: /etc/bash_completion.d/pacman - -# Bash completion for pacman -# Original: Manolis Tzanidakis -# -# Distributed under the terms of the GNU General Public License, v2 or later. -# - -## initial functions - -rem_selected () -{ - # (Adapted from bash_completion by Ian Macdonald ) - # This removes any options from the list of completions that have - # already been specified on the command line. - COMPREPLY=($(\echo "${COMP_WORDS[@]}" | \ - (while read -d ' ' i; do - [ "${i}" == "" ] && continue - # flatten array with spaces on either side, - # otherwise we cannot grep on word boundaries of - # first and last word - COMPREPLY=" ${COMPREPLY[@]} " - # remove word from list of completions - COMPREPLY=(${COMPREPLY/ ${i%% *} / }) +# This file is in the public domain. + +_arch_compgen() { + local i r + COMPREPLY=($(compgen -W '$*' -- "$cur")) + for ((i=1; i < ${#COMP_WORDS[@]}-1; i++)); do + for r in ${!COMPREPLY[@]}; do + if [[ ${COMP_WORDS[i]} = ${COMPREPLY[r]} ]]; then + unset 'COMPREPLY[r]'; break + fi done - \echo ${COMPREPLY[@]}))) - return 0 -} - -_available_repos () -{ - COMPREPLY=( $( compgen -W "$(\grep '\[' /etc/pacman.conf | \grep -v -e 'options' -e '^#' | \tr -d '[]' )" -- $cur ) ) -} - -_installed_pkgs () -{ - local installed_pkgs - installed_pkgs=$( \ls /var/lib/pacman/local/ ) - COMPREPLY=( $( compgen -W "$( for i in $installed_pkgs; do \echo ${i%-*-*}; done )" -- $cur ) ) -} - -_available_pkgs () -{ - #find balks easilly on a find /foo/*/* type dir, especially one like - # /var/lib/pacman/*/* - # This little change-up removes the find *and* only uses enabled repos - local available_pkgs - local enabled_repos - enabled_repos=$( \grep '\[' /etc/pacman.conf | \grep -v -e 'options' -e '^#' | \tr -d '[]' ) - available_pkgs=$( for r in $enabled_repos; do \echo /var/lib/pacman/sync/$r/*; done ) - COMPREPLY=( $( compgen -W "$( for i in $available_pkgs; do j=${i##*/}; echo ${j%-*-*}; done )" -- $cur ) ) + done } -_installed_groups () -{ - local installed_groups - installed_groups=$( \find /var/lib/pacman/local -name desc -exec \sed -ne '/%GROUPS%/,/^$/{//d; p}' {} \; | \sort -u ) - COMPREPLY=( $( compgen -W "$( for i in $installed_groups; do \echo ${i%-*-*}; done )" -- $cur ) ) +_arch_ptr2comp() { + local list= x y + for x; do + for y in '0 --' '1 -'; do + eval 'set -- ${'$x'[${y% *}]}' + list+=\ ${@/#/${y#* }} + done + done + _arch_compgen $list } -_available_groups () -{ - #find balks easilly on a find /foo/*/* type dir, especially one like - # /var/lib/pacman/*/* - # This little change-up removes the find *and* only uses enabled repos - local available_groups - local enabled_repos - enabled_repos=$( \grep '\[' /etc/pacman.conf | \grep -v -e 'options' -e '^#' | tr -d '[]' ) - available_groups=$( for r in $enabled_repos; do \sed '/%GROUPS%/,/^$/{//d; p}' /var/lib/pacman/sync/$r/*/desc | \sort -u; done ) - COMPREPLY=( $( compgen -W "$( for i in $available_groups; do \echo ${i%-*-*}; done )" -- $cur ) ) +_arch_incomp() { + local r="\s-(-${1#* }\s|\w*${1% *})"; [[ $COMP_LINE =~ $r ]] } -## makepkg completion - -_makepkg () -{ - local cur prev +_makepkg() { + local cur opts prev COMPREPLY=() - cur=${COMP_WORDS[COMP_CWORD]} + cur=$(_get_cword) prev=${COMP_WORDS[COMP_CWORD-1]} - - case "$prev" in - -p) - _filedir - return 0 - ;; - --help|--cleancache) - COMPREPLY='' - return 0 - ;; - esac - - if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '\ - -A --ignorearch \ - -b --builddeps \ - -c --clean \ - -C --cleancache \ - -d --nodeps \ - -e --noextract \ - -f --force \ - -g --geninteg \ - -h --help \ - -i --install \ - -L --log \ - -m --nocolor \ - -o --nobuild \ - -p \ - -r --rmdeps \ - -s --syncdeps \ - --asroot \ - --source \ - --noconfirm \ - --noprogressbar' -- $cur ) ) + if [[ $cur = -* && ! $prev =~ ^-(-(cleancache|config|help)$|\w*[Chp]) ]]; then + opts=('allsource asroot clean cleancache config force geninteg help holdver + ignorearch install log nobuild nocolor noconfirm nodeps noextract + noprogressbar pkg repackage rmdeps skipinteg source syncdeps' + 'A C L R c d e f g h i m o p r s') + _arch_ptr2comp opts fi - - rem_selected + true } -complete -o default -F _makepkg makepkg -## pacman completion - -_instring () -{ - str="${1}" - shift 1 - for c in "${@}"; do - if [ $(\expr index "${str}" "${c}") -gt 0 ]; then - return 0 +_pacman_pkg() { + _arch_compgen "$( + if [[ $2 ]]; then + \pacman -$1 | \cut -d' ' -f1 | \sort -u + else + \pacman -$1 fi - done - return 1 + )" } -_pacman () -{ - local a arg toparse op mod cur - COMPREPLY=() - - # This argument parsing is done so we can check for flag existance later - # right now it's a tad crappy, but does the job - for (( i=1; i < ${#COMP_WORDS[@]}-1; i++ )); do - a=${COMP_WORDS[i]} - arg="${a:0:2}" - toparse="${a:2}" - - case "${arg}" in - -@(U|R|S|Q|h|V)) - op="${arg/-}" - mod="${mod}${a:2}" - ;; - --) - arg="${a:2}" - case "${arg}" in - remove) op="R" ;; - upgrade) op="U" ;; - query) op="Q" ;; - sync) op="S" ;; - help) op="h" ;; - version) op="V" ;; - verbose) mod="${mod}v" ;; - root) mod="${mod}r" ;; - dbpath) mod="${mod}b" ;; - nodeps) mod="${mod}d" ;; - force) mod="${mod}f" ;; - groups) mod="${mod}g" ;; - info) mod="${mod}i" ;; - list) mod="${mod}l" ;; - print) mod="${mod}p" ;; - search) mod="${mod}s" ;; - sysupgrade) mod="${mod}u" ;; - upgrades) mod="${mod}u" ;; - downloadonly) mod="${mod}w" ;; - refresh) mod="${mod}y" ;; - changelog) mod="${mod}c" ;; - deps) mod="${mod}d" ;; - explicit) mod="${mod}e" ;; - unrequired) mod="${mod}t" ;; - foreign) mod="${mod}m" ;; - owns) mod="${mod}o" ;; - file) mod="${mod}p" ;; - search) mod="${mod}s" ;; - upgrades) mod="${mod}u" ;; - cascade) mod="${mod}c" ;; - check) mod="${mod}k" ;; - dbonly) mod="${mod}k" ;; - nosave) mod="${mod}n" ;; - recursive) mod="${mod}s" ;; - unneeded) mod="${mod}u" ;; - esac ;; - *) toparse="${a}" ;; - esac +_pacman_file() { + compopt -o filenames; _filedir 'pkg.tar.*' +} - arglen=$(( ${#toparse}-1 )) - for c in $(\seq 0 "${arglen}"); do - arg=${toparse:$c:1} - [ "${arg}" != "-" ] && mod="${mod}${arg}" - done +_pacman() { + local common core cur database prev query remove sync upgrade o + COMPREPLY=() + cur=$(_get_cword) + prev=${COMP_WORDS[COMP_CWORD-1]} + database=('asdeps asexplicit') + query=('changelog check deps explicit file foreign groups info list owns + search unrequired upgrades' 'c e g i k l m o p s t u') + remove=('cascade dbonly nodeps nosave print recursive unneeded' 'c k n p s u') + sync=('asdeps asexplicit clean downloadonly force groups ignore ignoregroup + info list needed nodeps print refresh search sysupgrade' + 'c f g i l p s u w y') + upgrade=('asdeps asexplicit force nodeps print' 'f p') + common=('arch cachedir config dbpath debug help logfile noconfirm + noprogressbar noscriptlet quiet root verbose' 'b d h q r v') + core=('database help query remove sync upgrade version' 'D Q R S U V h') + + for o in 'D database' 'Q query' 'R remove' 'S sync' 'U upgrade'; do + _arch_incomp "$o" && break done - cur=${COMP_WORDS[COMP_CWORD]} - - if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '\ - -h --help \ - -Q --query \ - -R --remove \ - -S --sync \ - -U --upgrade \ - -V --version \ - ' -- $cur ) ) - rem_selected - return 0 - fi - - if [[ "$cur" == -* ]]; then - case "${op}" in - U) - COMPREPLY=( $( compgen -W '\ - --asdeps \ - --asexplicit \ - -d --nodeps \ - -f --force \ - -h --help \ - --config \ - --logfile \ - --noconfirm \ - --noprogressbar \ - --noscriptlet \ - -v --verbose \ - -r --root \ - -b --dbpath \ - -p --print \ - --print-format \ - --cachedir \ - ' -- $cur ) ) - return 0 - ;; - R) - COMPREPLY=( $( compgen -W '\ - -c --cascade \ - -d --nodeps \ - -h --help \ - -k --dbonly \ - -n --nosave \ - -s --recursive \ - -u --unneeded \ - --config \ - --logfile \ - --noconfirm \ - --noprogressbar \ - --noscriptlet \ - -v --verbose \ - -r --root \ - -b --dbpath \ - -p --print \ - --print-format \ - --cachedir \ - ' -- $cur ) ) - return 0 - ;; - S) - COMPREPLY=( $( compgen -W '\ - --asdeps \ - --asexplicit \ - -c --clean \ - -d --nodeps \ - -f --force \ - -g --groups \ - -h --help \ - -i --info \ - -l --list \ - -s --search \ - -u --sysupgrade \ - -w --downloadonly \ - -y --refresh \ - --needed \ - --ignore \ - --ignoregroup \ - --config \ - --logfile \ - --noconfirm \ - --noprogressbar \ - --noscriptlet \ - -v --verbose \ - -r --root \ - -b --dbpath \ - -p --print \ - --print-format \ - --cachedir \ - ' -- $cur ) ) - return 0 - ;; - Q) - COMPREPLY=( $( compgen -W '\ - -c --changelog \ - -d --deps \ - -e --explicit \ - -g --groups \ - -h --help \ - -i --info \ - -k --check \ - -l --list \ - -m --foreign \ - -o --owns \ - -p --file \ - -s --search \ - -t --unrequired \ - -u --upgrades \ - --config \ - --logfile \ - --noconfirm \ - --noprogressbar \ - --noscriptlet \ - -v --verbose \ - -r --root \ - -b --dbpath \ - --cachedir \ - ' -- $cur ) ) - return 0 - ;; - esac - rem_selected - else - case "${op}" in - U) - COMPREPLY=( $( compgen -d -- "$cur" ) \ - $( compgen -f -X '!*.pkg.tar.*' -- "$cur" ) ) - return 0 - ;; - h|V) - COMPREPLY='' - return 0 - ;; + if [[ $? != 0 ]]; then + _arch_ptr2comp core + elif ! [[ $prev =~ ^-\w*[Vbhr] || + $prev = --@(cachedir|config|dbpath|help|logfile|root|version) ]] + then + [[ $cur = -* ]] && _arch_ptr2comp ${o#* } common || + case ${o% *} in + D|R) + _pacman_pkg Qq;; Q) - if _instring $mod g; then - _installed_groups - elif _instring $mod o; then - COMPREPLY=( $( compgen -d -- "$cur" ) \ - $( compgen -f -- "$cur" ) ) - elif _instring $mod p; then - COMPREPLY=( $( compgen -d -- "$cur" ) \ - $( compgen -f -X '!*.pkg.tar.*' -- "$cur" ) ) - elif _instring $mod u; then - COMPREPLY='' - return 0 - else - _installed_pkgs - fi - return 0 - ;; - R) - _installed_pkgs - return 0 - ;; + { _arch_incomp 'g groups' && _pacman_pkg Qg sort; } || + { _arch_incomp 'p file' && _pacman_file; } || + _arch_incomp 'o owns' || _arch_incomp 'u upgrades' || + _pacman_pkg Qq;; S) - if _instring $mod l; then - _available_repos - else - _available_pkgs - fi - return 0 - ;; - esac + { _arch_incomp 'g groups' && _pacman_pkg Sg; } || + { _arch_incomp 'l list' && _pacman_pkg Sl sort; } || + _pacman_pkg Slq;; + U) + _pacman_file;; + esac fi - - rem_selected + true } -complete -o filenames -F _pacman pacman + +complete -F _makepkg -o default makepkg +complete -F _pacman -o default pacman + +# ex:et ts=2 sw=2 ft=sh -- cgit v1.2.3-70-g09d2