diff --git a/cmd/helm/root.go b/cmd/helm/root.go index d40d9fa95..a03f77576 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -67,6 +67,11 @@ __helm_override_flags_to_kubectl_flags() echo "$1" | sed s/kube-context/context/ } +__helm_get_repos() +{ + eval $(__helm_binary_name) repo list 2>/dev/null | tail +2 | cut -f1 +} + __helm_get_contexts() { __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" @@ -106,6 +111,104 @@ __helm_binary_name() echo ${helm_binary} } +# This function prevents the zsh shell from adding a space after +# a completion by adding a second, fake completion +__helm_zsh_comp_nospace() { + __helm_debug "${FUNCNAME[0]}: in is ${in[*]}" + + local out in=("$@") + + # The shell will normally add a space after these completions. + # To avoid that we should use "compopt -o nospace". However, it is not + # available in zsh. + # Instead, we trick the shell by pretending there is a second, longer match. + # We only do this if there is a single choice left for completion + # to reduce the times the user could be presented with the fake completion choice. + + out=($(echo ${in[*]} | tr " " "\n" | \grep "^${cur}")) + __helm_debug "${FUNCNAME[0]}: out is ${out[*]}" + + [ ${#out[*]} -eq 1 ] && out+=("${out}.") + + __helm_debug "${FUNCNAME[0]}: out is now ${out[*]}" + + echo "${out[*]}" +} + +# $1 = 1 if the completion should include local charts (which means file completion) +__helm_list_charts() +{ + __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" + local repo url file out=() nospace=0 wantFiles=$1 + + # Handle completions for repos + for repo in $(__helm_get_repos); do + if [[ "${cur}" =~ ^${repo}/.* ]]; then + # We are doing completion from within a repo + out=$(eval $(__helm_binary_name) search repo ${cur} 2>/dev/null | cut -f1 | \grep ^${cur}) + nospace=0 + elif [[ ${repo} =~ ^${cur}.* ]]; then + # We are completing a repo name + out+=(${repo}/) + nospace=1 + fi + done + __helm_debug "${FUNCNAME[0]}: out after repos is ${out[*]}" + + # Handle completions for url prefixes + for url in https:// http:// file://; do + if [[ "${cur}" =~ ^${url}.* ]]; then + # The user already put in the full url prefix. Return it + # back as a completion to avoid the shell doing path completion + out="${cur}" + nospace=1 + elif [[ ${url} =~ ^${cur}.* ]]; then + # We are completing a url prefix + out+=(${url}) + nospace=1 + fi + done + __helm_debug "${FUNCNAME[0]}: out after urls is ${out[*]}" + + # Handle completion for files. + # We only do this if: + # 1- There are other completions found (if there are no completions, + # the shell will do file completion itself) + # 2- If there is some input from the user (or else we will end up + # lising the entire content of the current directory which will + # be too many choices for the user to find the real repos) + if [ $wantFiles -eq 1 ] && [ -n "${out[*]}" ] && [ -n "${cur}" ]; then + for file in $(\ls); do + if [[ ${file} =~ ^${cur}.* ]]; then + # We are completing a file prefix + out+=(${file}) + nospace=1 + fi + done + fi + __helm_debug "${FUNCNAME[0]}: out after files is ${out[*]}" + + # If the user didn't provide any input to completion, + # we provide a hint that a path can also be used + [ $wantFiles -eq 1 ] && [ -z "${cur}" ] && out+=(./ /) + + __helm_debug "${FUNCNAME[0]}: out after checking empty input is ${out[*]}" + + if [ $nospace -eq 1 ]; then + if [[ -n "${ZSH_VERSION}" ]]; then + # Don't let the shell add a space after the completion + local tmpout=$(__helm_zsh_comp_nospace "${out[@]}") + unset out + out=$tmpout + elif [[ $(type -t compopt) = "builtin" ]]; then + compopt -o nospace + fi + fi + + __helm_debug "${FUNCNAME[0]}: final out is ${out[*]}" + COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) +} + __helm_list_releases() { __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" @@ -123,7 +226,7 @@ __helm_list_repos() __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" local out # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h3) - if out=$(eval $(__helm_binary_name) repo list 2>/dev/null | tail +2 | cut -f1); then + if out=$(__helm_get_repos); then COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) fi } @@ -138,15 +241,47 @@ __helm_list_plugins() fi } +__helm_list_charts_after_name() { + __helm_debug "${FUNCNAME[0]}: last_command is $last_command" + if [[ ${#nouns[@]} -eq 1 ]]; then + __helm_list_charts 1 + fi +} + +__helm_list_releases_then_charts() { + __helm_debug "${FUNCNAME[0]}: last_command is $last_command" + if [[ ${#nouns[@]} -eq 0 ]]; then + __helm_list_releases + elif [[ ${#nouns[@]} -eq 1 ]]; then + __helm_list_charts 1 + fi +} + __helm_custom_func() { - __helm_debug "${FUNCNAME[0]}: last_command is $last_command" + __helm_debug "${FUNCNAME[0]}: last_command is $last_command" case ${last_command} in - helm_uninstall | helm_history | helm_status | helm_test |\ - helm_upgrade | helm_rollback | helm_get_*) + helm_pull) + __helm_list_charts 0 + return + ;; + helm_show_*) + __helm_list_charts 1 + return + ;; + helm_install | helm_template) + __helm_list_charts_after_name + return + ;; + helm_upgrade) + __helm_list_releases_then_charts + return + ;; + helm_uninstall | helm_history | helm_status | helm_test |\ + helm_rollback | helm_get_*) __helm_list_releases return - ;; + ;; helm_repo_remove) __helm_list_repos return