Skip to content

Commit

Permalink
fix(completions/*): scan until cword instead of ${#words[@]}-1
Browse files Browse the repository at this point in the history
We find in the codebase many loops scanning the arguments excluding
the last word (i.e., the loop condition is `i < ${#words[@]} - 1`).

The earliest versions of this type of loops were introduced in commit
e8d1508 in the form of `${#COMP_WORDS}-1` when the completions for
the `apt-*` commands are introduced.  There is no explanation about
the reason that the last word is excluded. [ Note: `${#COMP_WORDS}`
counting characters instead of words is clearly wrong but later fixed
to be `${#COMP_WORDS[@]}` in commit 57437d5. ]

Those are likely to be intended to exclude the word we currently try
to complete.  If that is the case, we should instead exclude all the
words at the current word and later words (i.e., the loop condition is
`i < cword`).  The value of `cword` matches `${#words[@]} - 1` in a
typical case where the tab completion is attempted at the end of the
command line, but this assumption breaks down when the user attempts a
completion in the middle of the command line.
  • Loading branch information
akinomyoga committed Sep 25, 2023
1 parent 531b751 commit 17e0cc3
Show file tree
Hide file tree
Showing 12 changed files with 12 additions and 12 deletions.
2 changes: 1 addition & 1 deletion completions/_yum
Expand Up @@ -45,7 +45,7 @@ _comp_cmd_yum()
_comp_initialize -s -- "$@" || return

local special="" i
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
if [[ ${words[i]} == @(install|update|upgrade|remove|erase|deplist|info) ]]; then
special=${words[i]}
break
Expand Down
2 changes: 1 addition & 1 deletion completions/apt-build
Expand Up @@ -6,7 +6,7 @@ _comp_cmd_apt_build()
_comp_initialize -- "$@" || return

local special="" i
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
if [[ ${words[i]} == @(install|remove|source|info|clean) ]]; then
special=${words[i]}
break
Expand Down
2 changes: 1 addition & 1 deletion completions/apt-get
Expand Up @@ -22,7 +22,7 @@ _comp_cmd_apt_get()
_comp_initialize -n ':=' -- "$@" || return

local special="" i
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
if [[ ${words[i]} == @(install|remove|auto?(-)remove|purge|source|build-dep|download|changelog) ]]; then
special=${words[i]}
break
Expand Down
2 changes: 1 addition & 1 deletion completions/apt-mark
Expand Up @@ -6,7 +6,7 @@ _comp_cmd_apt_mark()
_comp_initialize -s -- "$@" || return

local special="" i
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
if [[ ${words[i]} == @(auto|manual|minimize-manual|showauto|showmanual|hold|unhold|showhold|install|remove|deinstall|purge|showinstall|showremove|showpurge) ]]; then
special=${words[i]}
break
Expand Down
2 changes: 1 addition & 1 deletion completions/ip
Expand Up @@ -43,7 +43,7 @@ _comp_cmd_ip()
esac

local subcword cmd="" has_cmd="" subcmd=""
for ((subcword = 1; subcword < ${#words[@]} - 1; subcword++)); do
for ((subcword = 1; subcword < cword; subcword++)); do
[[ ${words[subcword]} == -b?(atch) ]] && return
[[ $has_cmd ]] && subcmd=${words[subcword]} && break
[[ ${words[subcword]} != -* &&
Expand Down
2 changes: 1 addition & 1 deletion completions/ipmitool
Expand Up @@ -66,7 +66,7 @@ _comp_cmd_ipmitool()
sel pef sol tsol isol user channel session sunoem kontronoem picmg fwum
firewall shell exec set hpm ekanalyzer)
local i c cmd="" has_cmd="" subcmd
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
[[ $has_cmd ]] && subcmd=${words[i]} && break
for c in "${cmds[@]}"; do
[[ ${words[i]} == "$c" ]] && cmd=$c has_cmd=set && break
Expand Down
2 changes: 1 addition & 1 deletion completions/jarsigner
Expand Up @@ -32,7 +32,7 @@ _comp_cmd_jarsigner()

# Check if a jar was already given.
local i jar=""
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
if [[ ${words[i]} == *.@(jar|apk) &&
${words[i - 1]} != -signedjar ]]; then
jar=set
Expand Down
2 changes: 1 addition & 1 deletion completions/mcrypt
Expand Up @@ -48,7 +48,7 @@ _comp_cmd_mcrypt()
_comp_compgen_filedir nc
else
local i decrypt=""
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
if [[ ${words[i]} == -@(d|-decrypt) ]]; then
_comp_compgen_filedir nc
decrypt=set
Expand Down
2 changes: 1 addition & 1 deletion completions/mdtool
Expand Up @@ -6,7 +6,7 @@ _comp_cmd_mdtool()
_comp_initialize -- "$@" || return

local command="" i
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
if [[ ${words[i]} == @(build|generate-makefiles|setup) ]]; then
command=${words[i]}
break
Expand Down
2 changes: 1 addition & 1 deletion completions/mr
Expand Up @@ -27,7 +27,7 @@ _comp_cmd_mr()
# Determine if user has entered an `mr` command. Used to block top-level
# (option and command) completions.
local cmd has_cmd="" i
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
# shellcheck disable=SC2053
if [[ ${words[i]} == $glob_commands ]]; then
cmd="${words[i]}"
Expand Down
2 changes: 1 addition & 1 deletion completions/pack200
Expand Up @@ -46,7 +46,7 @@ _comp_cmd_pack200()

# Check if a pack or a jar was already given.
local i pack="" jar=""
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
case ${words[i]} in
*.pack | *.pack.gz) pack=set ;;
*.jar) jar=set ;;
Expand Down
2 changes: 1 addition & 1 deletion completions/route
Expand Up @@ -17,7 +17,7 @@ _comp_cmd_route()
for opt in add del -host -net netmask metric mss window irtt reject mod \
dyn reinstate dev default gw; do
found=""
for ((i = 1; i < ${#words[@]} - 1; i++)); do
for ((i = 1; i < cword; i++)); do
[[ ${words[i]} == "$opt" ]] && found=set && break
done
[[ $found ]] || COMPREPLY+=("$opt")
Expand Down

0 comments on commit 17e0cc3

Please sign in to comment.