@@ -201,6 +201,8 @@ __%[1]s_extract_activeHelp() {
201201 local endIndex=${#activeHelpMarker}
202202
203203 while IFS='' read -r comp; do
204+ [[ -z $comp ]] && continue
205+
204206 if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then
205207 comp=${comp:endIndex}
206208 __%[1]s_debug "ActiveHelp found: $comp"
@@ -223,16 +225,21 @@ __%[1]s_handle_completion_types() {
223225 # If the user requested inserting one completion at a time, or all
224226 # completions at once on the command-line we must remove the descriptions.
225227 # https://github.com/spf13/cobra/issues/1508
226- local tab=$'\t' comp
227- while IFS='' read -r comp; do
228- [[ -z $comp ]] && continue
229- # Strip any description
230- comp=${comp%%%%$tab*}
231- # Only consider the completions that match
232- if [[ $comp == "$cur"* ]]; then
233- COMPREPLY+=("$comp")
234- fi
235- done < <(printf "%%s\n" "${completions[@]}")
228+
229+ # If there are no completions, we don't need to do anything
230+ (( ${#completions[@]} == 0 )) && return 0
231+
232+ local tab=$'\t'
233+
234+ # Strip any description and escape the completion to handled special characters
235+ IFS=$'\n' read -ra completions -d '' < <(printf "%%q\n" "${completions[@]%%%%$tab*}")
236+
237+ # Only consider the completions that match
238+ IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}")
239+
240+ # compgen looses the escaping so we need to escape all completions again since they will
241+ # all be inserted on the command-line.
242+ IFS=$'\n' read -ra COMPREPLY -d '' < <(printf "%%q\n" "${COMPREPLY[@]}")
236243 ;;
237244
238245 *)
@@ -243,11 +250,25 @@ __%[1]s_handle_completion_types() {
243250}
244251
245252__%[1]s_handle_standard_completion_case() {
246- local tab=$'\t' comp
253+ local tab=$'\t'
254+
255+ # If there are no completions, we don't need to do anything
256+ (( ${#completions[@]} == 0 )) && return 0
247257
248258 # Short circuit to optimize if we don't have descriptions
249259 if [[ "${completions[*]}" != *$tab* ]]; then
250- IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur")
260+ # First, escape the completions to handle special characters
261+ IFS=$'\n' read -ra completions -d '' < <(printf "%%q\n" "${completions[@]}")
262+ # Only consider the completions that match what the user typed
263+ IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}")
264+
265+ # compgen looses the escaping so, if there is only a single completion, we need to
266+ # escape it again because it will be inserted on the command-line. If there are multiple
267+ # completions, we don't want to escape them because they will be printed in a list
268+ # and we don't want to show escape characters in that list.
269+ if (( ${#COMPREPLY[@]} == 1 )); then
270+ COMPREPLY[0]=$(printf "%%q" "${COMPREPLY[0]}")
271+ fi
251272 return 0
252273 fi
253274
@@ -256,23 +277,39 @@ __%[1]s_handle_standard_completion_case() {
256277 # Look for the longest completion so that we can format things nicely
257278 while IFS='' read -r compline; do
258279 [[ -z $compline ]] && continue
259- # Strip any description before checking the length
260- comp=${compline%%%%$tab*}
280+
281+ # Before checking if the completion matches what the user typed,
282+ # we need to strip any description and escape the completion to handle special
283+ # characters because those escape characters are part of what the user typed.
284+ # Don't call "printf" in a sub-shell because it will be much slower
285+ # since we are in a loop.
286+ printf -v comp "%%q" "${compline%%%%$tab*}" &>/dev/null || comp=$(printf "%%q" "${compline%%%%$tab*}")
287+
261288 # Only consider the completions that match
262289 [[ $comp == "$cur"* ]] || continue
290+
291+ # The completions matches. Add it to the list of full completions including
292+ # its description. We don't escape the completion because it may get printed
293+ # in a list if there are more than one and we don't want show escape characters
294+ # in that list.
263295 COMPREPLY+=("$compline")
296+
297+ # Strip any description before checking the length, and again, don't escape
298+ # the completion because this length is only used when printing the completions
299+ # in a list and we don't want show escape characters in that list.
300+ comp=${compline%%%%$tab*}
264301 if ((${#comp}>longest)); then
265302 longest=${#comp}
266303 fi
267304 done < <(printf "%%s\n" "${completions[@]}")
268305
269- # If there is a single completion left, remove the description text
306+ # If there is a single completion left, remove the description text and escape any special characters
270307 if ((${#COMPREPLY[*]} == 1)); then
271308 __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
272- comp=" ${COMPREPLY[0]%%%%$tab*}"
273- __%[1]s_debug "Removed description from single completion, which is now: ${comp }"
274- COMPREPLY[0]=$comp
275- else # Format the descriptions
309+ COMPREPLY[0]=$(printf "%%q" " ${COMPREPLY[0]%%%%$tab*}")
310+ __%[1]s_debug "Removed description from single completion, which is now: ${COMPREPLY[0] }"
311+ else
312+ # Format the descriptions
276313 __%[1]s_format_comp_descriptions $longest
277314 fi
278315}
0 commit comments