-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(bash): Fix bash completion for suggestions that contain special characters. #2126
Changes from 5 commits
d131fa6
497d36c
76d900b
9771cb0
718f1d8
df206d9
7a9725b
2fc6dad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,19 +59,15 @@ __%[1]s_get_completion_results() { | |
# Prepare the command to request completions for the program. | ||
# Calling ${words[0]} instead of directly %[1]s allows handling aliases | ||
args=("${words[@]:1}") | ||
requestComp="${words[0]} %[2]s ${args[*]}" | ||
requestComp="${words[0]} %[2]s" | ||
if [[ "${#args[@]}" -gt 0 ]]; then | ||
requestComp+="$(printf " %%q" "${args[@]}")" | ||
fi | ||
|
||
lastParam=${words[$((${#words[@]}-1))]} | ||
lastChar=${lastParam:$((${#lastParam}-1)):1} | ||
__%[1]s_debug "lastParam ${lastParam}, lastChar ${lastChar}" | ||
|
||
if [[ -z ${cur} && ${lastChar} != = ]]; then | ||
# If the last parameter is complete (there is a space following it) | ||
# We add an extra empty parameter so we can indicate this to the go method. | ||
__%[1]s_debug "Adding extra empty parameter" | ||
requestComp="${requestComp} ''" | ||
fi | ||
|
||
# When completing a flag with an = (e.g., %[1]s -n=<TAB>) | ||
# bash focuses on the part after the =, so we need to remove | ||
# the flag part from $cur | ||
|
@@ -173,8 +169,9 @@ __%[1]s_process_completion_results() { | |
__%[1]s_handle_completion_types | ||
fi | ||
|
||
__%[1]s_handle_special_char "$cur" : | ||
__%[1]s_handle_special_char "$cur" = | ||
__%[1]s_handle_wordbreaks "$cur" | ||
|
||
__%[1]s_debug "The final COMPREPLY: $(printf "%%s\n" "${COMPREPLY[@]}")" | ||
|
||
# Print the activeHelp statements before we finish | ||
if ((${#activeHelp[*]} != 0)); then | ||
|
@@ -224,20 +221,28 @@ __%[1]s_handle_completion_types() { | |
# completions at once on the command-line we must remove the descriptions. | ||
# https://github.com/spf13/cobra/issues/1508 | ||
local tab=$'\t' comp | ||
while IFS='' read -r comp; do | ||
for comp in "${completions[@]}"; do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why this was needed so I didn't take it. |
||
[[ -z $comp ]] && continue | ||
# Strip any description | ||
comp=${comp%%%%$tab*} | ||
# Only consider the completions that match | ||
if [[ $comp == "$cur"* ]]; then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to do the escaping before we filter on |
||
COMPREPLY+=("$comp") | ||
fi | ||
done < <(printf "%%s\n" "${completions[@]}") | ||
fi | ||
done | ||
|
||
IFS=$'\n' read -ra COMPREPLY -d '' < <(printf "%%q\n" "${COMPREPLY[@]}") | ||
;; | ||
|
||
*) | ||
# Type: complete (normal completion) | ||
__%[1]s_handle_standard_completion_case | ||
|
||
# If there is a single completion left, escape the completion | ||
if ((${#COMPREPLY[@]} == 1)); then | ||
COMPREPLY[0]="$(printf "%%q" "${COMPREPLY[0]}")" | ||
fi | ||
|
||
;; | ||
esac | ||
} | ||
|
@@ -247,7 +252,13 @@ __%[1]s_handle_standard_completion_case() { | |
|
||
# Short circuit to optimize if we don't have descriptions | ||
if [[ "${completions[*]}" != *$tab* ]]; then | ||
IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur") | ||
# compgen's -W option respects shell quoting, so we need to escape. | ||
local compgen_words="$(printf "%%q\n" "${completions[@]}")" | ||
# compgen appears to respect shell quoting _after_ checking whether | ||
# they have the right prefix, so we also need to quote cur. | ||
local compgen_cur="$(printf "%%q" "${cur}")" | ||
IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${compgen_words}" -- "${compgen_cur}") | ||
|
||
return 0 | ||
fi | ||
|
||
|
@@ -271,21 +282,30 @@ __%[1]s_handle_standard_completion_case() { | |
__%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the loop above, we need to do the escaping before we filter on |
||
comp="${COMPREPLY[0]%%%%$tab*}" | ||
__%[1]s_debug "Removed description from single completion, which is now: ${comp}" | ||
COMPREPLY[0]=$comp | ||
COMPREPLY[0]="${comp}" | ||
else # Format the descriptions | ||
__%[1]s_format_comp_descriptions $longest | ||
fi | ||
} | ||
|
||
__%[1]s_handle_special_char() | ||
__%[1]s_handle_wordbreaks() | ||
{ | ||
if ((${#COMPREPLY[@]} == 0)); then | ||
return; | ||
fi | ||
|
||
local comp="$1" | ||
local char=$2 | ||
if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then | ||
local word=${comp%%"${comp##*${char}}"} | ||
local idx=${#COMPREPLY[*]} | ||
while ((--idx >= 0)); do | ||
COMPREPLY[idx]=${COMPREPLY[idx]#"$word"} | ||
local i prefix | ||
for ((i=0; i < ${#comp}; i++)); do | ||
local char="${comp:$i:1}" | ||
if [[ "${COMP_WORDBREAKS}" == *"${char}"* ]]; then | ||
prefix="${comp::$i+1}" | ||
fi | ||
done | ||
|
||
if [[ -n "${prefix}" ]]; then | ||
for ((i=0; i < ${#COMPREPLY[@]}; i++)); do | ||
COMPREPLY[i]=${COMPREPLY[i]#$prefix} | ||
done | ||
fi | ||
} | ||
|
@@ -342,6 +362,21 @@ __start_%[1]s() | |
|
||
COMPREPLY=() | ||
|
||
# Omit wordbreaks that would need to be escaped. | ||
local wordbreaks i | ||
for ((i=0; i < ${#COMP_WORDBREAKS}; i++)); do | ||
local char="${COMP_WORDBREAKS:$i:1}" | ||
if [[ $'\n\t ' == *"${char}"* ]]; then | ||
wordbreaks+="${char}" | ||
continue | ||
fi | ||
if [[ "${char}" == "$(printf "%%q" "${char}")" ]]; then | ||
wordbreaks+="${char}" | ||
continue | ||
fi | ||
done | ||
COMP_WORDBREAKS="${wordbreaks}" | ||
|
||
# Call _init_completion from the bash-completion package | ||
# to prepare the arguments properly | ||
if declare -F _init_completion >/dev/null 2>&1; then | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At surface value, this line looked wrong to me because I didn't think it was correct to handle wordbreaks like this when the user requested menu-completion. But after some manual testing I'm pleasantly surprised to find that this is correct, and menu-completion does still expect the same behavior as "regular" completion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't understand why this change was needed so for safety, I did not take it.