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 <stderr@mail.com>
Signed-off-by: Xavier Chantry <chantry.xavier@gmail.com>
Signed-off-by: Dan McGee <dan@archlinux.org>
This commit is contained in:
Andres P 2010-05-18 17:56:47 -04:30 committed by Dan McGee
parent ae73d75660
commit 80f7c1707c

View file

@ -1,370 +1,108 @@
# vim: set ft=sh ts=2 sw=2 et: # This file is in the public domain.
# file: /etc/bash_completion.d/pacman
# Bash completion for pacman _arch_compgen() {
# Original: Manolis Tzanidakis <mtzanidakis@freemail.gr> local i r
# COMPREPLY=($(compgen -W '$*' -- "$cur"))
# Distributed under the terms of the GNU General Public License, v2 or later. for ((i=1; i < ${#COMP_WORDS[@]}-1; i++)); do
# for r in ${!COMPREPLY[@]}; do
if [[ ${COMP_WORDS[i]} = ${COMPREPLY[r]} ]]; then
## initial functions unset 'COMPREPLY[r]'; break
fi
rem_selected ()
{
# (Adapted from bash_completion by Ian Macdonald <ian@caliban.org>)
# 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%% *} / })
done done
\echo ${COMPREPLY[@]}))) done
return 0
} }
_available_repos () _arch_ptr2comp() {
{ local list= x y
COMPREPLY=( $( compgen -W "$(\grep '\[' /etc/pacman.conf | \grep -v -e 'options' -e '^#' | \tr -d '[]' )" -- $cur ) ) for x; do
for y in '0 --' '1 -'; do
eval 'set -- ${'$x'[${y% *}]}'
list+=\ ${@/#/${y#* }}
done
done
_arch_compgen $list
} }
_installed_pkgs () _arch_incomp() {
{ local r="\s-(-${1#* }\s|\w*${1% *})"; [[ $COMP_LINE =~ $r ]]
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 () _makepkg() {
{ local cur opts prev
#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 ) )
}
_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 ) )
}
_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 ) )
}
## makepkg completion
_makepkg ()
{
local cur prev
COMPREPLY=() COMPREPLY=()
cur=${COMP_WORDS[COMP_CWORD]} cur=$(_get_cword)
prev=${COMP_WORDS[COMP_CWORD-1]} prev=${COMP_WORDS[COMP_CWORD-1]}
if [[ $cur = -* && ! $prev =~ ^-(-(cleancache|config|help)$|\w*[Chp]) ]]; then
case "$prev" in opts=('allsource asroot clean cleancache config force geninteg help holdver
-p) ignorearch install log nobuild nocolor noconfirm nodeps noextract
_filedir noprogressbar pkg repackage rmdeps skipinteg source syncdeps'
return 0 'A C L R c d e f g h i m o p r s')
;; _arch_ptr2comp opts
--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 ) )
fi fi
true
rem_selected
} }
complete -o default -F _makepkg makepkg
## pacman completion _pacman_pkg() {
_arch_compgen "$(
_instring () if [[ $2 ]]; then
{ \pacman -$1 | \cut -d' ' -f1 | \sort -u
str="${1}" else
shift 1 \pacman -$1
for c in "${@}"; do
if [ $(\expr index "${str}" "${c}") -gt 0 ]; then
return 0
fi fi
done )"
return 1
} }
_pacman () _pacman_file() {
{ compopt -o filenames; _filedir 'pkg.tar.*'
local a arg toparse op mod cur }
_pacman() {
local common core cur database prev query remove sync upgrade o
COMPREPLY=() 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')
# This argument parsing is done so we can check for flag existance later for o in 'D database' 'Q query' 'R remove' 'S sync' 'U upgrade'; do
# right now it's a tad crappy, but does the job _arch_incomp "$o" && break
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
arglen=$(( ${#toparse}-1 ))
for c in $(\seq 0 "${arglen}"); do
arg=${toparse:$c:1}
[ "${arg}" != "-" ] && mod="${mod}${arg}"
done
done done
cur=${COMP_WORDS[COMP_CWORD]} if [[ $? != 0 ]]; then
_arch_ptr2comp core
if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then elif ! [[ $prev =~ ^-\w*[Vbhr] ||
COMPREPLY=( $( compgen -W '\ $prev = --@(cachedir|config|dbpath|help|logfile|root|version) ]]
-h --help \ then
-Q --query \ [[ $cur = -* ]] && _arch_ptr2comp ${o#* } common ||
-R --remove \ case ${o% *} in
-S --sync \ D|R)
-U --upgrade \ _pacman_pkg Qq;;
-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) Q)
COMPREPLY=( $( compgen -W '\ { _arch_incomp 'g groups' && _pacman_pkg Qg sort; } ||
-c --changelog \ { _arch_incomp 'p file' && _pacman_file; } ||
-d --deps \ _arch_incomp 'o owns' || _arch_incomp 'u upgrades' ||
-e --explicit \ _pacman_pkg Qq;;
-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
;;
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
;;
S) S)
if _instring $mod l; then { _arch_incomp 'g groups' && _pacman_pkg Sg; } ||
_available_repos { _arch_incomp 'l list' && _pacman_pkg Sl sort; } ||
else _pacman_pkg Slq;;
_available_pkgs U)
fi _pacman_file;;
return 0 esac
;;
esac
fi fi
true
rem_selected
} }
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