diff --git a/bash_completion b/bash_completion index 12172ab3a29..9d745ba8d10 100644 --- a/bash_completion +++ b/bash_completion @@ -439,6 +439,7 @@ _comp_split() # @var[in] $? # @var[in] _var # @var[in] _append +# @var[in] _upvars # @return original $? _comp_compgen__error_fallback() { @@ -449,6 +450,10 @@ _comp_compgen__error_fallback() else eval -- "$_var=()" fi + if ((${#_upvars[@]})); then + _comp_unlocal "${_upvars[@]}" + _upvars=() + fi return "$_status" } @@ -478,6 +483,13 @@ _comp_compgen__error_fallback() # is ${cur-}. # -R The same as -c ''. Use raw outputs without filtering. # -C dir Evaluate compgen/generator in the specified directory. +# -P pref Prepend the prefix to the generated completions. Unlike `compgen +# -P prefix`, this prefix is subject to filtering by `cur`. When a +# non-empty prefix is specified, first `cur` is tested whether it is +# consistent with the prefix. Then, `cur` is reduced for the part +# excluding the prefix, and the normal completion generation is +# performed. Finally, the prefix is prepended to generated +# completions. # @var[in,opt] cur Used as the default value of a prefix to filter the # completions. # @@ -562,6 +574,7 @@ _comp_compgen() local _var= local _cur=${_comp_compgen__cur-${cur-}} local _dir="" + local _prefix="" local _ifs=$' \t\n' _has_ifs="" local _icmd="" _xcmd="" local -a _upvars=() @@ -572,7 +585,7 @@ _comp_compgen() shopt -u nocasematch fi local OPTIND=1 OPTARG="" OPTERR=0 _opt - while getopts ':av:U:Rc:C:lF:i:x:' _opt "$@"; do + while getopts ':av:U:Rc:C:P:lF:i:x:' _opt "$@"; do case $_opt in a) _append=set ;; v) @@ -601,6 +614,7 @@ _comp_compgen() fi _dir=$OPTARG ;; + P) _prefix=$OPTARG ;; l) _has_ifs=set _ifs=$'\n' ;; F) _has_ifs=set _ifs=$OPTARG ;; [ix]) @@ -636,6 +650,19 @@ _comp_compgen() [[ $_append ]] || _append=${_comp_compgen__append-} fi + local _prefix_fail="" + if [[ $_prefix ]]; then + if [[ $_cur == "$_prefix"* ]]; then + _cur=${_cur#"$_prefix"} + elif [[ $_prefix == "$_cur"* ]]; then + _cur="" + else + # No completions are generated because the current word does not match + # the prefix. + _prefix_fail=set + fi + fi + if [[ $1 != -* ]]; then # usage: _comp_compgen [options] NAME args if [[ $_has_ifs ]]; then @@ -657,6 +684,11 @@ _comp_compgen() fi shift + if [[ $_prefix_fail ]]; then + _comp_compgen__error_fallback + return 1 + fi + _comp_compgen__call_generator "$@" else # usage: _comp_compgen [options] -- [compgen_options] @@ -680,6 +712,11 @@ _comp_compgen() return 2 fi + if [[ $_prefix_fail ]]; then + _comp_compgen__error_fallback + return 1 + fi + _comp_compgen__call_builtin "$@" fi } @@ -694,8 +731,6 @@ _comp_compgen() # @var[in] _var _comp_compgen__call_generator() { - ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" - if [[ $_dir ]]; then local _original_pwd=$PWD local PWD=${PWD-} OLDPWD=${OLDPWD-} @@ -708,14 +743,28 @@ _comp_compgen__call_generator() } fi + ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" + local _comp_compgen__append=$_append local _comp_compgen__var=$_var local _comp_compgen__cur=$_cur cur=$_cur + if [[ $_prefix ]]; then + local -a tmp=() + local _comp_compgen__var=tmp + local _comp_compgen__append="" + fi # Note: we use $1 as a part of a function name, and we use $2... as # arguments to the function if any. # shellcheck disable=SC2145 "${_generator[@]}" "$@" local _status=$? + if [[ $_prefix ]]; then + local _i + for _i in "${!tmp[@]}"; do + tmp[_i]=$_prefix${tmp[_i]} + done + _comp_compgen -RU tmp ${_append:+-a} -v "$_var" -- -W '"${tmp[@]}"' + fi # Go back to the original directory. # Note: Failure of this line results in the change of the current @@ -770,6 +819,13 @@ if ((BASH_VERSINFO[0] > 5 || BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3)); t ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" ((${#_result[@]})) || return + + if [[ $_prefix ]]; then + local _i + for _i in "${!_result[@]}"; do + _result[_i]=$_prefix${_result[_i]} + done + fi if [[ $_append ]]; then eval -- "$_var+=(\"\${_result[@]}\")" else @@ -794,7 +850,13 @@ else } ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" - _comp_split -l ${_append:+-a} "$_var" "$_result" + + if [[ $_prefix ]]; then + _comp_split -l ${_append:+-a} "$_var" \ + "$(IFS=$'\n' compgen -W '$_result' -P "$_prefix")" + else + _comp_split -l ${_append:+-a} "$_var" "$_result" + fi } fi @@ -1852,7 +1914,7 @@ _comp_compgen_tilde() { if [[ ${cur-} == \~* && $cur != */* ]]; then # Try generate ~username completions - if _comp_compgen -c "${cur#\~}" -- -P '~' -u; then + if _comp_compgen -P '~' -- -u; then # 2>/dev/null for direct invocation, e.g. in the # _comp_compgen_tilde unit test compopt -o filenames 2>/dev/null diff --git a/completions/7z b/completions/7z index 027f84be652..7333ba605e7 100644 --- a/completions/7z +++ b/completions/7z @@ -15,20 +15,17 @@ _comp_cmd_7z() case $cur in -ao*) - _comp_compgen -c "${cur:3}" -- -P"${cur:0:3}" -W 'a s t u' + _comp_compgen -P "-ao" -- -W 'a s t u' return ;; -?(a)[ix]*) - local opt - if [[ $cur == -a[ix]* ]]; then - opt=${cur:0:3} cur=${cur:3} - else - opt=${cur:0:2} cur=${cur:2} - fi if [[ $cur != *[@\!]* ]]; then - _comp_compgen -- -P"$opt" -W '@ ! r@ r-@ r0@ r! r-! r0!' - elif [[ $cur == ?(r@(-|0|))@* ]]; then - _comp_compgen -c "${cur#*@}" -- -P"${opt}${cur%%@*}@" -f + [[ $cur =~ ^-a?[ix] ]] + local prefix=${BASH_REMATCH-} + _comp_compgen -P "$prefix" -- -W '@ ! r@ r-@ r0@ r! r-! r0!' + elif [[ $cur =~ ^-a?[ix](r|r-|r0)?@ ]]; then + local prefix=${BASH_REMATCH-} + _comp_compgen -P "$prefix" -- -f compopt -o filenames fi return @@ -43,31 +40,30 @@ _comp_cmd_7z() ;; -o* | -w?*) compopt -o filenames - _comp_compgen -c "${cur:2}" -- -d -P"${cur:0:2}" -S/ + _comp_compgen -P "${cur:0:2}" -- -d -S/ compopt -o nospace return ;; -r?*) - _comp_compgen -c "${cur:2}" -- -P"${cur:0:2}" -W '- 0' + _comp_compgen -P "-r" -- -W '- 0' return ;; -scs*) - _comp_compgen -c "${cur:4}" -- -P"${cur:0:4}" -W 'UTF-8 WIN DOS' + _comp_compgen -P "-scs" -- -W 'UTF-8 WIN DOS' return ;; -ssc?*) - _comp_compgen -c "${cur:4}" -- -P"${cur:0:4}" -W '-' + _comp_compgen -P "-ssc" -- -W '-' return ;; -t*) if [[ $mode == w ]]; then - _comp_compgen -c "${cur:2}" -- -P"${cur:0:2}" -W '7z bzip2 gzip - swfc tar wim xz zip' + _comp_compgen -P "-t" -- -W '7z bzip2 gzip swfc tar wim xz zip' else - _comp_compgen -c "${cur:2}" -- -P"${cur:0:2}" -W '7z apm arj - bzip2 cab chm cpio cramfs deb dmg elf fat flv gzip hfs iso - lzh lzma lzma86 macho mbr mslz mub nsis ntfs pe ppmd rar - rpm squashfs swf swfc tar udf vhd wim xar xz z zip' + _comp_compgen -P "-t" -- -W '7z apm arj bzip2 cab chm cpio + cramfs deb dmg elf fat flv gzip hfs iso lzh lzma lzma86 + macho mbr mslz mub nsis ntfs pe ppmd rar rpm squashfs swf + swfc tar udf vhd wim xar xz z zip' fi return ;; diff --git a/completions/_mount b/completions/_mount index ab385f11288..23ef20515b1 100644 --- a/completions/_mount +++ b/completions/_mount @@ -44,7 +44,7 @@ _comp_cmd_mount() host=${cur#//} host=${host%%/*} if [[ $host ]]; then - _comp_compgen -c "${cur#//"$host"}" split -P "//$host" -- "$( + _comp_compgen -P "//$host" split -- "$( smbclient -d 0 -NL "$host" 2>/dev/null | command sed -ne '/^[[:blank:]]*Sharename/,/^$/p' | command sed -ne '3,$s|^[^A-Za-z]*\([^[:blank:]]*\).*$|/\1|p' diff --git a/completions/_mount.linux b/completions/_mount.linux index 3d4c30794b4..4ad2ae23401 100644 --- a/completions/_mount.linux +++ b/completions/_mount.linux @@ -8,24 +8,18 @@ _comp_cmd_mount() local cur prev words cword comp_args _comp_initialize -n =: -- "$@" || return - local split="" case "$prev" in -t | --types) # find /lib/modules/$(uname -r)/ -type f -path '*/fs/*.ko' -printf '%f\n' | cut -d. -f1 # FIXME: no - if [[ $cur == ?*,* ]]; then - prev="${cur%,*}" - cur="${cur##*,}" - split=set - fi - _comp_compgen -- -W 'auto adfs affs autofs btrfs cifs coda cramfs - davfs debugfs devpts efs ext2 ext3 ext4 fuse hfs hfsplus hpfs - iso9660 jffs2 jfs minix msdos ncpfs nfs nfs4 ntfs ntfs-3g proc - qnx4 ramfs reiserfs romfs squashfs smbfs sysv tmpfs ubifs udf - ufs umsdos usbfs vfat xfs' - _comp_compgen -a fstypes - [[ $split ]] && ((${#COMPREPLY[@]})) && - _comp_compgen -Rv COMPREPLY -- -P "$prev," -W '"${COMPREPLY[@]}"' + [[ $cur =~ ^.+, ]] + local prefix=${BASH_REMATCH-} + _comp_compgen -P "$prefix" -- -W 'auto adfs affs autofs btrfs cifs + coda cramfs davfs debugfs devpts efs ext2 ext3 ext4 fuse hfs + hfsplus hpfs iso9660 jffs2 jfs minix msdos ncpfs nfs nfs4 ntfs + ntfs-3g proc qnx4 ramfs reiserfs romfs squashfs smbfs sysv + tmpfs ubifs udf ufs umsdos usbfs vfat xfs' + _comp_compgen -aP "$prefix" fstypes return ;; --bind | -B | --rbind | -R) @@ -66,61 +60,58 @@ _comp_cmd_mount() # no is not a real fstype, reset to "auto" [[ $fstype == no?* ]] && fstype=auto # split options list - if [[ $cur == ?*,* ]]; then - prev="${cur%,*}" - cur="${cur##*,}" - split=set - fi + [[ $cur =~ ^.+, ]] + local prefix=${BASH_REMATCH-} # no completion if $cur is opt=smth - [[ $cur == *=* ]] && return + [[ ${cur##*,} == *=* ]] && return # mount options - _comp_compgen -- -W 'loop {,a}sync {,no}atime {,no}auto - {,fs,def,root}context= defaults {,no}dev {,no}diratime dirsync - {,no}exec group {,no}iversion {,no}mand _netdev nofail - {,no}relatime {,no}strictatime {,no}suid owner remount ro rw - {,no}user users' + _comp_compgen -P "$prefix" -- -W 'loop {,a}sync {,no}atime + {,no}auto {,fs,def,root}context= defaults {,no}dev + {,no}diratime dirsync {,no}exec group {,no}iversion {,no}mand + _netdev nofail {,no}relatime {,no}strictatime {,no}suid owner + remount ro rw {,no}user users' case "$fstype" in adfs | auto) - _comp_compgen -a -- -W '{u,g}id= {own,oth}mask=' + _comp_compgen -aP "$prefix" -- -W '{u,g}id= {own,oth}mask=' ;;& affs | auto) - _comp_compgen -a -- -W '{u,g}id= set{u,g}id= mode= protect - usemp verbose prefix= volume= reserved= root= bs= - {,no,usr,grp}quota' + _comp_compgen -aP "$prefix" -- -W '{u,g}id= set{u,g}id= + mode= protect usemp verbose prefix= volume= reserved= + root= bs= {,no,usr,grp}quota' ;;& btrfs | auto) - _comp_compgen -a -- -W 'degraded subvol= subvolid= device= - nodatasum nodatacow nobarrier max_inline= alloc_start= - thread_pool= compress= compress-force= ssd noacl - notreelog flushoncommit metadata_ratio= - {,no}space_cache clear_cache user_subvol_rm_allowed - autodefrag inode_cache' + _comp_compgen -aP "$prefix" -- -W 'degraded subvol= + subvolid= device= nodatasum nodatacow nobarrier + max_inline= alloc_start= thread_pool= compress= + compress-force= ssd noacl notreelog flushoncommit + metadata_ratio= {,no}space_cache clear_cache + user_subvol_rm_allowed autodefrag inode_cache' ;;& cifs | auto) - _comp_compgen -a -- -W 'user= password= credentials= - {u,g}id= force{u,g}id port= servern= netbiosname= - {file,dir}_mode= ip= domain= guest iocharset - {,no}setuids {,no,dyn}perm directio {,no}mapchars - {,no}intr hard soft noacl nocase sec= nobrl sfu - {,no}serverino nounix nouser_xattr {r,w}size= + _comp_compgen -aP "$prefix" -- -W 'user= password= + credentials= {u,g}id= force{u,g}id port= servern= + netbiosname= {file,dir}_mode= ip= domain= guest + iocharset {,no}setuids {,no,dyn}perm directio + {,no}mapchars {,no}intr hard soft noacl nocase sec= + nobrl sfu {,no}serverino nounix nouser_xattr {r,w}size= rwpidforward backup{u,g}id cache=' ;;& davfs | auto) - _comp_compgen -a -- -W 'conf= {file,dir}_mode= {u,g}id= - username=' + _comp_compgen -aP "$prefix" -- -W 'conf= {file,dir}_mode= + {u,g}id= username=' ;;& ext[2-4] | auto) - _comp_compgen -a -- -W '{,no}acl bsddf minixdf check= debug - errors= {,no}grpid {bsd,sysv}groups {,no,usr,grp}quota - nobh nouid32 oldalloc orlov res{u,g}id= sb= - {,no}user_xattr' + _comp_compgen -aP "$prefix" -- -W '{,no}acl bsddf minixdf + check= debug errors= {,no}grpid {bsd,sysv}groups + {,no,usr,grp}quota nobh nouid32 oldalloc orlov + res{u,g}id= sb= {,no}user_xattr' ;;& ext[34] | auto) - _comp_compgen -a -- -W 'journal= journal_dev= norecovery - noload data= barrier= commit=' + _comp_compgen -aP "$prefix" -- -W 'journal= journal_dev= + norecovery noload data= barrier= commit=' ;;& ext4 | auto) - _comp_compgen -a -- -W 'journal_checksum + _comp_compgen -aP "$prefix" -- -W 'journal_checksum journal_async_commit nobarrier inode_readahead= stripe= {,no}delalloc abort {max,min}_batch_time= journal_ioprio= {,no}auto_da_alloc {,no}discard nouid32 @@ -128,83 +119,87 @@ _comp_cmd_mount() max_dir_size_kb= i_version' ;;& msdos | umsdos | vfat | auto) - _comp_compgen -a -- -W 'blocksize= {u,g}id= {u,d,f}mask= - allow_utime= check= codepage= conv= cvf_format= - cvf_option= debug fat= iocharset= tz= quiet showexec - sys_immutable flush usefree {,no}dots dotsOK=' + _comp_compgen -aP "$prefix" -- -W 'blocksize= {u,g}id= + {u,d,f}mask= allow_utime= check= codepage= conv= + cvf_format= cvf_option= debug fat= iocharset= tz= quiet + showexec sys_immutable flush usefree {,no}dots dotsOK=' ;;& vfat | auto) - _comp_compgen -a -- -W 'uni_xlate posix nonumtail utf8 - shortname=' + _comp_compgen -aP "$prefix" -- -W 'uni_xlate posix + nonumtail utf8 shortname=' ;;& iso9660 | auto) - _comp_compgen -a -- -W 'norock nojoliet check= {u,g}id= - map= mode= unhide block= conv= cruft session= sbsector= - iocharset= utf8' + _comp_compgen -aP "$prefix" -- -W 'norock nojoliet check= + {u,g}id= map= mode= unhide block= conv= cruft session= + sbsector= iocharset= utf8' ;;& jffs2 | auto) - _comp_compgen -a -- -W 'compr= rp_size=' + _comp_compgen -aP "$prefix" -- -W 'compr= rp_size=' ;;& jfs | auto) - _comp_compgen -a -- -W 'iocharset= resize= {,no}integrity - errors= {,no,usr,grp}quota' + _comp_compgen -aP "$prefix" -- -W 'iocharset= resize= + {,no}integrity errors= {,no,usr,grp}quota' ;;& nfs | nfs4 | auto) - _comp_compgen -a -- -W 'soft hard timeo= retrans= {r,w}size= - {,no}ac acreg{min,max}= acdir{min,max}= actimeo= bg fg - retry= sec= {,no}sharecache {,no}resvport lookupcache= - proto= port= {,no}intr {,no}cto {,nfs}vers=' + _comp_compgen -aP "$prefix" -- -W 'soft hard timeo= + retrans= {r,w}size= {,no}ac acreg{min,max}= + acdir{min,max}= actimeo= bg fg retry= sec= + {,no}sharecache {,no}resvport lookupcache= proto= port= + {,no}intr {,no}cto {,nfs}vers=' ;;& nfs | auto) - _comp_compgen -a -- -W 'udp tcp rdma mount{port,proto,host}= - mountvers= namlen={,no}lock {,no}acl {,no}rdirplus - {,no}fsc' + _comp_compgen -aP "$prefix" -- -W 'udp tcp rdma + mount{port,proto,host}= mountvers= namlen={,no}lock + {,no}acl {,no}rdirplus {,no}fsc' ;;& nfs4 | auto) - _comp_compgen -a -- -W 'clientaddr= {,no}migration' + _comp_compgen -aP "$prefix" -- -W 'clientaddr= + {,no}migration' ;;& ntfs-3g) - _comp_compgen -a -- -W '{u,g}id= {u,f,d}mask= usermapping= - permissions inherit locale= force {,no}recover - ignore_case remove_hiberfile show_sys_files - hide_{hid,dot}_files windows_names allow_other - max_read= silent no_def_opts streams_interface= - user_xattr efs_raw {,no}compression debug no_detach' + _comp_compgen -aP "$prefix" -- -W '{u,g}id= {u,f,d}mask= + usermapping= permissions inherit locale= force + {,no}recover ignore_case remove_hiberfile + show_sys_files hide_{hid,dot}_files windows_names + allow_other max_read= silent no_def_opts + streams_interface= user_xattr efs_raw {,no}compression + debug no_detach' ;;& proc | auto) - _comp_compgen -a -- -W '{u,g}id=' + _comp_compgen -aP "$prefix" -- -W '{u,g}id=' ;;& reiserfs | auto) - _comp_compgen -a -- -W 'conv hash= + _comp_compgen -aP "$prefix" -- -W 'conv hash= {,no_un}hashed_relocation noborder nolog notail replayonly resize= user_xattr acl barrier=' ;;& tmpfs | auto) - _comp_compgen -a -- -W 'size= nr_blocks= nr_inodes= mode= - {u,g}id= mpol=' + _comp_compgen -aP "$prefix" -- -W 'size= nr_blocks= + nr_inodes= mode= {u,g}id= mpol=' ;;& udf | auto) - _comp_compgen -a -- -W '{u,g}id= umask= unhide undelete - nostrict iocharset bs= novrs session= anchor= volume= - partition= lastblock= fileset= rootdir=' + _comp_compgen -aP "$prefix" -- -W '{u,g}id= umask= unhide + undelete nostrict iocharset bs= novrs session= anchor= + volume= partition= lastblock= fileset= rootdir=' ;;& usbfs | auto) - _comp_compgen -a -- -W 'dev{u,g}id= devmode= bus{u,g}id= - busmode= list{u,g}id= listmode=' + _comp_compgen -aP "$prefix" -- -W 'dev{u,g}id= devmode= + bus{u,g}id= busmode= list{u,g}id= listmode=' ;;& xfs | auto) - _comp_compgen -a -- -W 'allocsize= {,no}attr2 barrier dmapi - {,no}grpid {bsd,sysv}groups ihashsize= {,no}ikeep - inode{32,64} {,no}largeio logbufs= logbsize= logdev= - rtdev= mtpt= noalign norecovery nouuid osyncisosync - {u,g,p}qnoenforce {,u,usr,g,grp,p,prj}quota sunit= - swidth= swalloc' + _comp_compgen -aP "$prefix" -- -W 'allocsize= {,no}attr2 + barrier dmapi {,no}grpid {bsd,sysv}groups ihashsize= + {,no}ikeep inode{32,64} {,no}largeio logbufs= logbsize= + logdev= rtdev= mtpt= noalign norecovery nouuid + osyncisosync {u,g,p}qnoenforce + {,u,usr,g,grp,p,prj}quota sunit= swidth= swalloc' ;;& esac # COMP_WORDBREAKS is a real pain in the ass - prev="${prev##*["$COMP_WORDBREAKS"]}" - [[ $split ]] && ((${COMPREPLY[@]})) && - _comp_compgen -Rv COMPREPLY -- -P "$prev," -W '"${COMPREPLY[@]}"' + if ((${#COMPREPLY[@]})) && [[ $prefix == *["$COMP_WORDBREAKS"]* ]]; then + local tmp=${prefix%["$COMP_WORDBREAKS"]*} + COMPREPLY=("${COMPREPLY[@]#"$tmp"?}") + fi [[ ${COMPREPLY-} == *= ]] && compopt -o nospace return ;; @@ -236,7 +231,7 @@ _comp_cmd_mount() host=${cur#//} host=${host%%/*} if [[ $host ]]; then - _comp_compgen -c "${cur#//"$host"}" split -P "//$host" -- "$( + _comp_compgen -P "//$host" split -- "$( smbclient -d 0 -NL "$host" 2>/dev/null | command sed -ne '/^[[:blank:]]*Sharename/,/^$/p' | command sed -ne '3,$s|^[^A-Za-z]*\([^[:blank:]]*\).*$|/\1|p' diff --git a/completions/_umount.linux b/completions/_umount.linux index 5540e8eac65..d5692f46af1 100644 --- a/completions/_umount.linux +++ b/completions/_umount.linux @@ -98,20 +98,14 @@ _comp_cmd_umount() case "$prev" in -t) # FIXME: no - local split="" - if [[ $cur == ?*,* ]]; then - prev="${cur%,*}" - cur="${cur##*,}" - split=set - fi - _comp_compgen -- -W 'adfs affs autofs btrfs cifs coda cramfs - debugfs devpts efs ext2 ext3 ext4 fuse hfs hfsplus hpfs iso9660 - jfs minix msdos ncpfs nfs nfs4 ntfs ntfs-3g proc qnx4 ramfs - reiserfs romfs squashfs smbfs sysv tmpfs ubifs udf ufs umsdos - usbfs vfat xfs' - _comp_compgen -a fstypes - [[ $split ]] && ((${#COMPREPLY[@]})) && - _comp_compgen -Rv COMPREPLY -- -P "$prev," -W '"${COMPREPLY[@]}"' + [[ $cur =~ ^.+, ]] + local prefix=${BASH_REMATCH-} + _comp_compgen -P "$prefix" -- -W 'adfs affs autofs btrfs cifs coda + cramfs debugfs devpts efs ext2 ext3 ext4 fuse hfs hfsplus hpfs + iso9660 jfs minix msdos ncpfs nfs nfs4 ntfs ntfs-3g proc qnx4 + ramfs reiserfs romfs squashfs smbfs sysv tmpfs ubifs udf ufs + umsdos usbfs vfat xfs' + _comp_compgen -aP "$prefix" fstypes return ;; -O) diff --git a/completions/curl b/completions/curl index 96e4b221edd..dc61bc4ccf6 100644 --- a/completions/curl +++ b/completions/curl @@ -53,13 +53,12 @@ _comp_cmd_curl() --data | --data-ascii | --data-binary | --data-urlencode | --json | \ --header | --proxy-header | --write-out | -${noargopts}[dHw]) if [[ $cur == \@* ]]; then - _comp_compgen -c "${cur:1}" filedir - _comp_compgen -a -c "${cur:1}" -- -W '-' - if [[ ${#COMPREPLY[@]} -eq 1 && -d ${COMPREPLY[0]} ]]; then + _comp_compgen -P @ filedir + _comp_compgen -aP @ -- -W '-' + if [[ ${#COMPREPLY[@]} -eq 1 && -d ${COMPREPLY[0]#@} ]]; then COMPREPLY[0]+=/ compopt -o nospace fi - COMPREPLY=("${COMPREPLY[@]/#/@}") fi return ;; diff --git a/completions/dot b/completions/dot index dcb84369b28..8f1a56366a8 100644 --- a/completions/dot +++ b/completions/dot @@ -13,21 +13,20 @@ _comp_cmd_dot() ;; -T*) # generate langs - _comp_compgen -c "${cur#-T}" split -P "-T" -- "$( + _comp_compgen -P "-T" split -- "$( "$1" -TNON_EXISTENT 2>&1 | command sed -ne 's/.*one of://p' )" return ;; -K*) # generate layouts - _comp_compgen -c "${cur#-K}" split -P "-K" -- "$( + _comp_compgen -P "-K" split -- "$( "$1" -KNON_EXISTENT 2>&1 | command sed -ne 's/.*one of://p' )" return ;; -o*) - _comp_compgen -c "${cur#-o}" filedir && - COMPREPLY=("${COMPREPLY[@]/#/-o}") + _comp_compgen -P "-o" filedir return ;; esac diff --git a/completions/gprof b/completions/gprof index cb186330c95..798df5a3358 100644 --- a/completions/gprof +++ b/completions/gprof @@ -10,12 +10,11 @@ _comp_cmd_gprof() return ;; -S*) - _comp_compgen -c "${cur:2}" filedir - ((${#COMPREPLY[@]})) && COMPREPLY=("${COMPREPLY[@]/#/-S}") + _comp_compgen -P "-S" filedir return ;; -O*) - _comp_compgen -c "${cur:2}" -- -P -O -W 'auto bsd 4.4bsd magic + _comp_compgen -P "-O" -- -W 'auto bsd 4.4bsd magic prof' return ;; diff --git a/completions/kcov b/completions/kcov index 20a62f22c5e..573a48aa86a 100644 --- a/completions/kcov +++ b/completions/kcov @@ -28,17 +28,23 @@ _comp_cmd_kcov() return ;; --limits | -l) - if [[ $cur == ?*,* ]]; then - prev="${cur%,*}" - cur="${cur##*,}" - _comp_compgen -- -W "{0..100}" - ((${#COMPREPLY[@]} == 1)) && - _comp_compgen -Rv COMPREPLY -- -P "$prev," -W '"$COMPREPLY"' + # Complete the option argument of the form "--limits low,high". + local prefix + if [[ $cur =~ ^.+, ]]; then + prefix=$BASH_REMATCH else - _comp_compgen -- -W "{0..100}" - ((${#COMPREPLY[@]} == 1)) && COMPREPLY=("${COMPREPLY/%/,}") + prefix="" compopt -o nospace fi + _comp_compgen -P "$prefix" -- -W "{0..100}" && + if ((${#COMPREPLY[@]} == 1)); then + # When we complete the "low" part, we suffix a comma. + [[ $prefix ]] || COMPREPLY=("${COMPREPLY[@]/%/,}") + else + # When the result is not unique, we do not show the prefix + # part on the list. + COMPREPLY=("${COMPREPLY[@]##*,}") + fi return ;; --title | -t | --include-pattern | --exclude-pattern | --path-strip-level) diff --git a/completions/lintian b/completions/lintian index 51402b2a86f..ac3fb7af316 100644 --- a/completions/lintian +++ b/completions/lintian @@ -12,10 +12,9 @@ _comp_cmd_lintian__tags() tags=$(command sed -e "s/\<$item\>//g" <<<"$tags") done _comp_compgen -aR -- -W "$tags" - elif [[ $cur == *,* ]]; then - _comp_compgen -ac "${cur##*,}" -- -P "${cur%,*}," -W "$tags" else - _comp_compgen -a -- -W "$tags" + [[ $cur == ^.*, ]] + _comp_compgen -aP "${BASH_REMATCH-}" -- -W "$tags" fi } @@ -37,10 +36,9 @@ _comp_cmd_lintian__checks() done done _comp_compgen -aR -- -W "$checks" - elif [[ $cur == *,* ]]; then - _comp_compgen -ac "${cur##*,}" -- -P "${cur%,*}," -W "$checks" else - _comp_compgen -a -- -W "$checks" + [[ $cur == ^.*, ]] + _comp_compgen -aP "${BASH_REMATCH-}" -- -W "$checks" fi } @@ -57,10 +55,9 @@ _comp_cmd_lintian__infos() infos=$(command sed -e "s/\<$item\>//g" <<<"$infos") done _comp_compgen -aR -- -W "$infos" - elif [[ $cur == *,* ]]; then - _comp_compgen -ac "${cur##*,}" -- -P "${cur%,*}," -W "$infos" else - _comp_compgen -a -- -W "$infos" + [[ $cur == ^.*, ]] + _comp_compgen -aP "${BASH_REAMTCH-}" -- -W "$infos" fi } diff --git a/completions/protoc b/completions/protoc index ac93e1cab74..9f8c1b54d9f 100644 --- a/completions/protoc +++ b/completions/protoc @@ -31,13 +31,11 @@ _comp_cmd_protoc() case $cur in -o*) - _comp_compgen -c "${cur:2}" filedir - ((${#COMPREPLY[@]})) && COMPREPLY=("${COMPREPLY[@]/#/-o}") + _comp_compgen -P "-o" filedir return ;; -I*) - _comp_compgen -c "${cur:2}" filedir -d - ((${#COMPREPLY[@]})) && COMPREPLY=("${COMPREPLY[@]/#/-I}") + _comp_compgen -P "-I" filedir -d return ;; -*) diff --git a/completions/ssh b/completions/ssh index db30ba340f7..90ea9c403a8 100644 --- a/completions/ssh +++ b/completions/ssh @@ -371,9 +371,7 @@ _comp_cmd_ssh() esac if [[ $cur == -F* ]]; then - _comp_compgen -c "${cur#-F}" filedir - # Prefix completions with '-F' - COMPREPLY=("${COMPREPLY[@]/#/-F}") + _comp_compgen -P "-F" filedir elif [[ $cur == -* ]]; then _comp_compgen_usage else @@ -447,9 +445,7 @@ _comp_cmd_sftp() esac if [[ $cur == -F* ]]; then - _comp_compgen -c "${cur#-F}" filedir - # Prefix completions with '-F' - COMPREPLY=("${COMPREPLY[@]/#/-F}") + _comp_compgen -P "-F" filedir elif [[ $cur == -* ]]; then _comp_compgen_usage else diff --git a/completions/strings b/completions/strings index 52464a80d62..228da782d38 100644 --- a/completions/strings +++ b/completions/strings @@ -37,13 +37,10 @@ _comp_cmd_strings() command sed -e "s/\[-number\]//")" [[ ${COMPREPLY-} == *= ]] && compopt -o nospace return - elif [[ $cur == @* ]]; then - _comp_compgen -c "${cur:1}" filedir && - COMPREPLY=("${COMPREPLY[@]/#/@}") - return fi - _comp_compgen_filedir + [[ $cur =~ ^@ ]] + _comp_compgen -P "${BASH_REMATCH-}" filedir } && complete -F _comp_cmd_strings strings diff --git a/completions/tshark b/completions/tshark index b7e2f6a398c..9da06391b78 100644 --- a/completions/tshark +++ b/completions/tshark @@ -26,8 +26,7 @@ _comp_cmd_tshark() _comp_cmd_tshark__prefs="$("$1" -G defaultprefs 2>/dev/null | command sed -ne 's/^#\{0,1\}\([a-z0-9_.-]\{1,\}:\).*/\1/p' | tr '\n' ' ')" : ${prefix:=} - _comp_compgen -c "${cur:${#prefix}}" -- -P "$prefix" \ - -W "$_comp_cmd_tshark__prefs" + _comp_compgen -P "$prefix" -- -W "$_comp_cmd_tshark__prefs" [[ ${COMPREPLY-} == *: ]] && compopt -o nospace fi return @@ -101,8 +100,7 @@ _comp_cmd_tshark() if [[ ${cur:${#prefix}} == lua_script:* ]]; then _comp_compgen -c "${cur#*:}" filedir lua else - _comp_compgen -c "${cur:${#prefix}}" -- -P "$prefix" \ - -W 'lua_script:' + _comp_compgen -P "$prefix" -- -W 'lua_script:' [[ ${COMPREPLY-} == *: ]] && compopt -o nospace fi return diff --git a/completions/upgradepkg b/completions/upgradepkg index 0012f6282c3..30f6fb9e232 100644 --- a/completions/upgradepkg +++ b/completions/upgradepkg @@ -10,13 +10,11 @@ _comp_cmd_upgradepkg() return fi - if [[ $cur == ?*%* ]]; then - prev="${cur%%?(\\)%*}" - cur="${cur#*%}" - local nofiles="" + if [[ $cur =~ ^[^%]+% ]]; then + local prefix=$BASH_REMATCH nofiles="" compopt -o filenames - _comp_compgen -- -P "$prev%" -f -X "!*.t[bgxl]z" || nofiles=set - _comp_compgen -a -- -P "$prev%" -S '/' -d + _comp_compgen -P "$prefix" -- -f -X "!*.t[bgxl]z" || nofiles=set + _comp_compgen -aP "$prefix" -- -S '/' -d [[ $nofiles ]] && compopt -o nospace return fi diff --git a/completions/xgamma b/completions/xgamma index 2a0c40973f5..a265cd5ae79 100644 --- a/completions/xgamma +++ b/completions/xgamma @@ -32,8 +32,8 @@ _comp_cmd_xgamma() # local screen numbers local t screens=$(xrandr --query 2>/dev/null | command sed -ne \ '/^Screen /s|^Screen \{1,\}\(.*\):.*$|\1|p' 2>/dev/null) - t="${cur#:}" - _comp_compgen -c "${cur##*.}" -- -P "${t%.*}." -W '$screens' + cur="${cur#:}" + _comp_compgen -P "${cur%.*}." -- -W '$screens' elif [[ $cur != *:* ]]; then # complete hostnames _comp_compgen_known_hosts -c -- "$cur" diff --git a/doc/api-and-naming.md b/doc/api-and-naming.md index 824e351e154..c82179365ce 100644 --- a/doc/api-and-naming.md +++ b/doc/api-and-naming.md @@ -143,12 +143,12 @@ concept is later extended to also mean "eXported". The generator functions, which have names of the form `_comp_compgen_NAME`, are used to generate completion candidates. A generator function is supposed to be -called by `_comp_compgen [OPTS] NAME ARGS` where `OPTS = -aRl|-v var|-c cur|-C -dir|-F sep` are the options to modify the behavior (see the code comment of -`_comp_compgen` for details). When there are no `opts`, the generator function -is supposed to be directly called as `_comp_compgen_NAME ARGS`. The result is -stored in the target variable (which is `COMPREPLY` by default but can be -specified by `-v var` in `OPTS`). +called by `_comp_compgen [OPTS] NAME ARGS` where `OPTS = -aR|-v var|-c cur|-C +dir|-U var|-P pref` are the options to modify the behavior (see the code +comment of `_comp_compgen` for details). When there are no `opts`, the +generator function is supposed to be directly called as `_comp_compgen_NAME +ARGS`. The result is stored in the target variable (which is `COMPREPLY` by +default but can be specified by `-v var` in `OPTS`). ### Implementing a generator function diff --git a/test/t/unit/test_unit_compgen.py b/test/t/unit/test_unit_compgen.py index fb04d7472af..310609dc6e0 100644 --- a/test/t/unit/test_unit_compgen.py +++ b/test/t/unit/test_unit_compgen.py @@ -172,3 +172,49 @@ def test_9_inherit_a(self, bash, functions): bash, "_comp__test_compgen gen9", want_output=True ) assert output.strip() == "<11><11>" + + def test_10_option_P_unmatching_prefix(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'x' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert output.strip() == "" + + def test_10_option_P_incomplete_prefix(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'pre' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert ( + output.strip() + == "" + ) + + def test_10_option_P_exact_prefix(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'prefix,' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert ( + output.strip() + == "" + ) + + def test_10_option_P_starts_with_prefix(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'prefix,a' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert output.strip() == "" + + def test_10_option_P_no_match(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'prefix,x' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert output.strip() == ""