perlpunk / shell-completions

Collection of shell (bash, zsh) tab completion scripts for various command line tools
MIT License
36 stars 7 forks source link

Execution of shell-completion causes change in console `IFS` value #2

Open alexiswl opened 3 years ago

alexiswl commented 3 years ago

Hello,

I have an example yaml and output bash script from running the appspec completion command with the --bash parameter.

Command run

Click to expand!

The bash script is sourced in my ~/.bash_profile.

Example yaml file

Click to expand! ```yaml --- name: ica-context-switcher title: Switch contexts options: - name: project-name summary: | Name of the project type: string completion: command_string: |- cat "$HOME/.ica-ica-lazy/tokens/tokens.json" | jq -r 'keys[]' - name: scope summary: | The privilege level you'd like to associate with the token type: string enum: [ admin, read-only ] ```

Example bash completion script

Click to expand! ```bash #!/usr/bin/env bash # Generated with perl module App::Spec v0.013 _ica-context-switcher() { COMPREPLY=() local program=ica-context-switcher local cur prev words cword _init_completion -n : || return declare -a FLAGS declare -a OPTIONS declare -a MYWORDS local INDEX=`expr $cword - 1` MYWORDS=("${words[@]:1:$cword}") FLAGS=('--help' 'Show command help' '-h' 'Show command help') OPTIONS=('--project-name' 'Name of the project ' '--scope' 'The privilege level you'"\\'"'d like to associate with the token ') __ica-context-switcher_handle_options_flags case ${MYWORDS[$INDEX-1]} in --project-name) _ica-context-switcher__option_project_name_completion ;; --scope) _ica-context-switcher_compreply "admin" "read-only" return ;; esac case $INDEX in *) __comp_current_options || return ;; esac } _ica-context-switcher_compreply() { local prefix="" cur="$(printf '%q' "$cur")" IFS=$'\n' COMPREPLY=($(compgen -P "$prefix" -W "$*" -- "$cur")) __ltrim_colon_completions "$prefix$cur" # http://stackoverflow.com/questions/7267185/bash-autocompletion-add-description-for-possible-completions if [[ ${#COMPREPLY[*]} -eq 1 ]]; then # Only one completion COMPREPLY=( "${COMPREPLY[0]%% -- *}" ) # Remove ' -- ' and everything after COMPREPLY=( "${COMPREPLY[0]%%+( )}" ) # Remove trailing spaces fi } _ica-context-switcher__option_project_name_completion() { local CURRENT_WORD="${words[$cword]}" local param_project_name="$(cat "$HOME/.ica-ica-lazy/tokens/tokens.json" | jq -r 'keys[]')" _ica-context-switcher_compreply "$param_project_name" } __ica-context-switcher_dynamic_comp() { local argname="$1" local arg="$2" local name desc cols desclength formatted local comp=() local max=0 while read -r line; do name="$line" desc="$line" name="${name%$'\t'*}" if [[ "${#name}" -gt "$max" ]]; then max="${#name}" fi done <<< "$arg" while read -r line; do name="$line" desc="$line" name="${name%$'\t'*}" desc="${desc/*$'\t'}" if [[ -n "$desc" && "$desc" != "$name" ]]; then # TODO portable? cols=`tput cols` [[ -z $cols ]] && cols=80 desclength=`expr $cols - 4 - $max` formatted=`printf "%-*s -- %-*s" "$max" "$name" "$desclength" "$desc"` comp+=("$formatted") else comp+=("'$name'") fi done <<< "$arg" _ica-context-switcher_compreply ${comp[@]} } function __ica-context-switcher_handle_options() { local i j declare -a copy local last="${MYWORDS[$INDEX]}" local max=`expr ${#MYWORDS[@]} - 1` for ((i=0; i<$max; i++)) do local word="${MYWORDS[$i]}" local found= for ((j=0; j<${#OPTIONS[@]}; j+=2)) do local option="${OPTIONS[$j]}" if [[ "$word" == "$option" ]]; then found=1 i=`expr $i + 1` break fi done if [[ -n $found && $i -lt $max ]]; then INDEX=`expr $INDEX - 2` else copy+=("$word") fi done MYWORDS=("${copy[@]}" "$last") } function __ica-context-switcher_handle_flags() { local i j declare -a copy local last="${MYWORDS[$INDEX]}" local max=`expr ${#MYWORDS[@]} - 1` for ((i=0; i<$max; i++)) do local word="${MYWORDS[$i]}" local found= for ((j=0; j<${#FLAGS[@]}; j+=2)) do local flag="${FLAGS[$j]}" if [[ "$word" == "$flag" ]]; then found=1 break fi done if [[ -n $found ]]; then INDEX=`expr $INDEX - 1` else copy+=("$word") fi done MYWORDS=("${copy[@]}" "$last") } __ica-context-switcher_handle_options_flags() { __ica-context-switcher_handle_options __ica-context-switcher_handle_flags } __comp_current_options() { local always="$1" if [[ -n $always || ${MYWORDS[$INDEX]} =~ ^- ]]; then local options_spec='' local j= for ((j=0; j<${#FLAGS[@]}; j+=2)) do local name="${FLAGS[$j]}" local desc="${FLAGS[$j+1]}" options_spec+="$name"$'\t'"$desc"$'\n' done for ((j=0; j<${#OPTIONS[@]}; j+=2)) do local name="${OPTIONS[$j]}" local desc="${OPTIONS[$j+1]}" options_spec+="$name"$'\t'"$desc"$'\n' done __ica-context-switcher_dynamic_comp 'options' "$options_spec" return 1 else return 0 fi } complete -o default -F _ica-context-switcher ica-context-switcher ```

If I run the following command on a new console:

$ IFS="$IFS" python -c "import os; print(repr(os.environ['IFS']))"
'  \t\n'

But I then call the auto-completion script

ica-context-switcher --<tab><tab>

I get a different value when trying to get the IFS output

$ IFS="$IFS" python -c "import os; print(repr(os.environ['IFS']))"
'\n'

I am running through WSL2 and this hasn't caused me any troubles, however, any colleague that runs bash on MacOS and then installs autocompletion with brew install bash_completion@2 ends up with the following issue: https://github.com/scop/bash-completion/issues/515

Is there a way for IFS to be reset to the default after the completion script has been activated?

Work around has been to run brew install bash_completion@2 --HEAD since https://github.com/scop/bash-completion/issues/515 has been resolved by https://github.com/scop/bash-completion/pull/519

perlpunk commented 3 years ago

Thanks for the bugreport. Indeed that shouldn't be the case, and hopefully I can just fix it by using local, too

alexiswl commented 1 year ago

Setting local works, but beware of autocompletions, other autocompletions may call. If they haven't been patched then this will still have the same issue.

So

_<name>_compreply() {
    local prefix=""
    cur="$(printf '%q' "$cur")"
    IFS=$'\n' COMPREPLY=($(compgen -P "$prefix" -W "$*" -- "$cur"))
    ...

to

_<name>_compreply() {
    local prefix=""
    local IFS=$'\n'
    cur="$(printf '%q' "$cur")"
    IFS=$'\n' COMPREPLY=($(compgen -P "$prefix" -W "$*" -- "$cur"))
    ...

seems to work