Sdkman Incorrect zsh completion script output

1.5k views Asked by At

I am using oh-my-zsh and I have been trying to develop a custom completion script for sdkman.

However I have encountered a small problem when trying to mutualize some of the commands.

Below is the beginning of the completion script. There are three functions using the _describe method to output a completion help.

#compdef sdk

zstyle ':completion:*:descriptions' format '%B%d%b'

# Gets candidate lists and removes all unecessery things just to get candidate names
__get_candidate_list() {
  echo `sdk list | grep --color=never  "$ sdk install" | sed 's/\$ sdk install //g' | sed -e 's/[\t ]//g;/^$/d'`
}

__get_current_installed_list() {
  echo `sdk current | sed "s/Using://g" | sed "s/\:.*//g"  | sed -e "s/[\t ]//g;/^$/d"`
}

__describe_commands() {
  local -a commands
  commands=(
    'install: install a program'
    'uninstall: uninstal an existing program'
  )

  _describe -t commands "Commands" commands && ret=0
}

__describe_install() {
  local -a candidate_list
  candidate_list=( $( __get_candidate_list ) )
  _describe -t candidate_list "Candidates available" candidate_list && ret=0
}

__describe_uninstall() { # FIXME THis is not working, it only displays candidate list
  local -a candidates_to_uninstall
  candidates_to_uninstall=( $( __get_current_installed_list ) )
  _describe -t candidates_to_uninstall "Uninstallable candidates" candidates_to_uninstall && ret=0
}

The __get_candidate_list echoes the following values:

ant asciidoctorj bpipe ceylon crash cuba cxf gaiden glide gradle grails groovy groovyserv infrastructor java jbake kotlin kscript lazybones leiningen maven micronaut sbt scala spark springboot sshoogr vertx visualvm

The __get_current_installed_list echoes the following values:

gradle java kotlin maven sbt scala

The second part of the script below is where we call everything so that the completion script is used correctly by zsh:

function _sdk() {
  local ret=1
  local target=$words[2]

  _arguments -C \
    '1: :->first_arg' \
    '2: :->second_arg' \
    && ret=0

    case $state in
      first_arg)
        __describe_commands
        ;;
      second_arg)
        case $target in
          install)
            __describe_install
            ;;
          uninstall)
            __describe_uninstall
            ;;
          *)
            ;;
        esac
        ;;
    esac

    return $ret
}

_sdk "$@"

The problem is the following: when I type sdk install I get the right output, the one from the __get_candidate_list method BUT when I use sdk uninstall it still gives me the output from __get_candidate_list althought I am expecting __get_current_installed_list output.

EDIT: After a bit of debugging, it seems that zsh is not at fault here. I can't figure out why, but sdkman gives me the same output with both sdk list and sdk current (After the sed commands) from inside the completion script. IN my shell, both commands work properly with shell.

Is there something wrong with the way I use the _describe method ? Is there anything else I am not seeing ?

Thanks for your help.

1

There are 1 answers

1
matthieusb On BEST ANSWER

So I finally found a workaround to fix this but it is not ideal.

I chose to launch the commands in the background when launching the plugin, and fill text files with the results, so that completion scripts can use these after. Below is the code I used in the zsh-sdkman.plugin.zsh file, in case my github repository disappears:

# --------------------------
# -------- Executed on shell launch for completion help
# --------------------------
# NOTE: Sdkman seems to always output the candidate list rather than the currently installted list when we do this through the completion script
# There are two goals in the code below:
#   - Optimization: the _sdkman_get_candidate_list command can take time, so it is done once and in background
#   - Bug correction: correct the problem with sdkman command output explained above

# WARNING: We are setting this as a local variable because we don't have it yet at the time of initialization
# A better approach would be welcome
SDKMAN_DIR_LOCAL=~/.sdkman

# Custom variables for later
export ZSH_SDKMAN_CANDIDATE_LIST_HOME=~/.zsh-sdkman.candidate-list
export ZSH_SDKMAN_INSTALLED_LIST_HOME=~/.zsh-sdkman.current-installed-list

_sdkman_get_candidate_list() {
  (sdk list | grep --color=never  "$ sdk install" | sed 's/\$ sdk install //g' | sed -e 's/[\t ]//g;/^$/d' > $ZSH_SDKMAN_CANDIDATE_LIST_HOME &)
}

_sdkman_get_current_installed_list() {
  (sdk current | sed "s/Using://g" | sed "s/\:.*//g"  | sed -e "s/[\t ]//g;/^$/d" > $ZSH_SDKMAN_INSTALLED_LIST_HOME &)
}

# "sdk" command is not found if we don't do this
source "$SDKMAN_DIR_LOCAL/bin/sdkman-init.sh"

# Initialize files with available candidate list and currently installted candidates
_sdkman_get_candidate_list "$@"
_sdkman_get_current_installed_list "$@"

For more information, you can see the complete repository of my plugin: https://github.com/matthieusb/zsh-sdkman

If you have another cleaner solution, I'll be willing to make the necessary modifications, or don't hesitate to make a pull request on the project.