From bc7fd662dc433a96d69a803bda7805881ae660de Mon Sep 17 00:00:00 2001 From: Oliver Weiler Date: Mon, 25 Oct 2021 13:35:30 +0200 Subject: [PATCH] Unify Bash's and zsh's programmable completion (#1002) * Unify Bash's and zsh's programmable completion * Perform autocompletion for short options * Use SDKMAN_CANDIDATES for resolving candidates * Handle `read` edge case --- contrib/completion/bash/sdk | 30 ++++----- contrib/completion/zsh/sdk | 61 ------------------- src/main/bash/sdkman-init.sh | 4 +- .../groovy/sdkman/specs/CompletionSpec.groovy | 13 +--- 4 files changed, 21 insertions(+), 87 deletions(-) delete mode 100644 contrib/completion/zsh/sdk diff --git a/contrib/completion/bash/sdk b/contrib/completion/bash/sdk index 9cdb241a..17fc6e3a 100644 --- a/contrib/completion/bash/sdk +++ b/contrib/completion/bash/sdk @@ -1,11 +1,11 @@ #!/usr/bin/bash _sdk() { - local -r previous_word="${COMP_WORDS[COMP_CWORD - 1]}" - local -r current_word="${COMP_WORDS[COMP_CWORD]}" + local -r previous_word=${COMP_WORDS[COMP_CWORD - 1]} + local -r current_word=${COMP_WORDS[COMP_CWORD]} if ((COMP_CWORD == 3)); then - local -r before_previous_word="${COMP_WORDS[COMP_CWORD - 2]}" + local -r before_previous_word=${COMP_WORDS[COMP_CWORD - 2]} __sdkman_complete_candidate_version "$before_previous_word" "$previous_word" "$current_word" @@ -25,19 +25,18 @@ __sdkman_complete_command() { sdk) candidates=("install" "uninstall" "list" "use" "config" "default" "home" "env" "current" "upgrade" "version" "broadcast" "help" "offline" "selfupdate" "update" "flush") ;; - current|default|home|uninstall|upgrade|use) + current|c|default|d|home|h|uninstall|rm|upgrade|ug|use|u) local -r candidate_paths=("${SDKMAN_CANDIDATES_DIR}"/*) for candidate_path in "${candidate_paths[@]}"; do - candidates+=(${candidate_path##*/}) + candidates+=("${candidate_path##*/}") done ;; - install|list) - local -r all_candidates=$(curl --silent "${SDKMAN_CANDIDATES_API}/candidates/all") - IFS=',' read -r -a candidates <<< "$all_candidates" + install|i|list|ls) + candidates=${SDKMAN_CANDIDATES[@]} ;; - env) - candidates=("init install clear") + env|e) + candidates=("init" "install" "clear") ;; offline) candidates=("enable" "disable") @@ -61,18 +60,19 @@ __sdkman_complete_candidate_version() { local -a candidates case $command in - use|default|home|uninstall) + default|d|home|h|uninstall|rm|use|u) local -r version_paths=("${SDKMAN_CANDIDATES_DIR}/${candidate}"/*) for version_path in "${version_paths[@]}"; do [[ $version_path = *current ]] && continue - candidates+=(${version_path##*/}) + candidates+=("${version_path##*/}") done ;; - install) - local -r all_candidate_versions=$(curl --silent "${SDKMAN_CANDIDATES_API}/candidates/$candidate/${SDKMAN_PLATFORM}/versions/all") - IFS=',' read -r -a candidates <<< "$all_candidate_versions" + install|i) + while IFS= read -r -d, version || [[ -n "$version" ]]; do + candidates+=("$version") + done <<< "$(curl --silent "${SDKMAN_CANDIDATES_API}/candidates/$candidate/${SDKMAN_PLATFORM}/versions/all")" ;; esac diff --git a/contrib/completion/zsh/sdk b/contrib/completion/zsh/sdk deleted file mode 100644 index b6d311d7..00000000 --- a/contrib/completion/zsh/sdk +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/zsh - -_sdk() { - case "${CURRENT}" in - 2) - compadd -X $'Commands:\n' -- "${${(Mk)functions[@]:#__sdk_*}[@]#__sdk_}" - compadd -n rm - ;; - 3) - case "${words[2]}" in - l|ls|list|i|install) - compadd -X $'Candidates:\n' -- "${SDKMAN_CANDIDATES[@]}" - ;; - ug|upgrade|h|home|c|current|u|use|d|default|rm|uninstall) - compadd -X $'Installed Candidates:\n' -- "${${(u)${(f)$(find -L -- "${SDKMAN_CANDIDATES_DIR}" -mindepth 2 -maxdepth 2 -type d)}[@]:h}[@]:t}" - ;; - e|env) - compadd init install clear - ;; - config) - ;; - offline) - compadd enable disable - ;; - selfupdate) - compadd force - ;; - flush) - compadd archives broadcast tmp version - ;; - esac - ;; - 4) - case "${words[2]}" in - i|install) - setopt localoptions kshglob - if [[ "${words[3]}" == 'java' ]]; then - compadd -X $'Installable Versions of java:\n' -- "${${${${${(f)$(__sdkman_list_versions "${words[3]}")}[@]:5:-4}[@]:#* | (local only|installed ) | *}[@]##* | | }[@]%%+( )}" - else - compadd -X "Installable Versions of ${words[3]}:"$'\n' -- "${${(z)${(M)${(f)${$(__sdkman_list_versions "${words[3]}")//[*+>]+( )/-}}[@]:# *}[@]}[@]:#-*}" - fi - ;; - h|home|u|use|d|default|rm|uninstall) - compadd -X "Installed Versions of ${words[3]}:"$'\n' -- "${${(f)$(find -L -- "${SDKMAN_CANDIDATES_DIR}/${words[3]}" -mindepth 1 -maxdepth 1 -type d -not -name 'current')}[@]:t}" - ;; - esac - ;; - 5) - case "${words[2]}" in - i|install) - _files -X "Path to Local Installation of ${words[3]} ${words[4]}:"$'\n' -/ - ;; - esac - ;; - esac -} - -compdef _sdk sdk - -# Set 'sdkman_auto_complete' to 'true' in .sdkman/etc/config to enable completion - diff --git a/src/main/bash/sdkman-init.sh b/src/main/bash/sdkman-init.sh index be8229e1..8004653e 100644 --- a/src/main/bash/sdkman-init.sh +++ b/src/main/bash/sdkman-init.sh @@ -190,7 +190,9 @@ if [[ "$sdkman_auto_complete" == 'true' ]]; then compinit fi fi - source "${SDKMAN_DIR}/contrib/completion/zsh/sdk" + autoload -U bashcompinit + bashcompinit + source "${SDKMAN_DIR}/contrib/completion/bash/sdk" __sdkman_echo_debug "ZSH completion script loaded..." elif [[ "$bash_shell" == 'true' ]]; then source "${SDKMAN_DIR}/contrib/completion/bash/sdk" diff --git a/src/test/groovy/sdkman/specs/CompletionSpec.groovy b/src/test/groovy/sdkman/specs/CompletionSpec.groovy index 5991e8cc..7ef0eb04 100644 --- a/src/test/groovy/sdkman/specs/CompletionSpec.groovy +++ b/src/test/groovy/sdkman/specs/CompletionSpec.groovy @@ -4,14 +4,11 @@ import sdkman.support.SdkmanEnvSpecification class CompletionSpec extends SdkmanEnvSpecification { static final String CANDIDATES_API = "http://localhost:8080/2" - static final String BROADCAST_API_LATEST_ID_ENDPOINT = "$CANDIDATES_API/broadcast/latest/id" - static final String CANDIDATES_ALL_ENDPOINT = "$CANDIDATES_API/candidates/all" def "should complete the list of commands"() { given: bash = sdkmanBashEnvBuilder - .withVersionCache("x.y.z") .withConfiguration("sdkman_auto_complete", "true") .build() @@ -28,11 +25,8 @@ class CompletionSpec extends SdkmanEnvSpecification { def "should complete the list of candidates"() { given: - curlStub.primeWith(BROADCAST_API_LATEST_ID_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155") - .primeWith(CANDIDATES_ALL_ENDPOINT, "echo java,groovy") - bash = sdkmanBashEnvBuilder - .withVersionCache("x.y.z") + .withCandidates(["java", "groovy"]) .withConfiguration("sdkman_auto_complete", "true") .build() @@ -41,7 +35,7 @@ class CompletionSpec extends SdkmanEnvSpecification { when: bash.execute("COMP_CWORD=2; COMP_WORDS=(sdk install); _sdk") - bash.execute("echo \${COMPREPLY[@]}") + bash.execute('echo "\${COMPREPLY[@]}"') then: bash.output.contains("java groovy") @@ -55,7 +49,6 @@ class CompletionSpec extends SdkmanEnvSpecification { unameStub.forKernel("Darwin").forMachine("x86_64") bash = sdkmanBashEnvBuilder - .withVersionCache("x.y.z") .withConfiguration("sdkman_auto_complete", "true") .withUnameStub(unameStub) .build() @@ -65,7 +58,7 @@ class CompletionSpec extends SdkmanEnvSpecification { when: bash.execute("COMP_CWORD=3; COMP_WORDS=(sdk install java); _sdk") - bash.execute("echo \${COMPREPLY[@]}") + bash.execute('echo "\${COMPREPLY[@]}"') then: bash.output.contains("16.0.1.hs-adpt 17.0.0-tem")