Skip to content
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

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

Open
alexiswl opened this issue Jun 17, 2021 · 2 comments
Open

Comments

@alexiswl
Copy link

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!
---
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!
#!/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: scop/bash-completion#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 scop/bash-completion#515 has been resolved by scop/bash-completion#519

@perlpunk
Copy link
Owner

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

@alexiswl
Copy link
Author

alexiswl commented Oct 6, 2022

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

alexiswl added a commit to umccr/ica-ica-lazy that referenced this issue Oct 7, 2022
Stops IFS being set in environment.
Solves: 
* perlpunk/shell-completions#2
* scop/bash-completion#515
alexiswl added a commit to umccr/cwl-ica-cli that referenced this issue Oct 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants