Open FrederikNJS opened 4 years ago
Hello, I just stumbled upon this issue as I'm having the same problem.
Running source <(kubectl completion zsh)
in the rc file is not possible unless a global version is set, as the command is not "present" (setting a global version is particularly problematic in the case of kubectl
as compatibility between local kubectl
version and remote Kubernetes cluster has some version compatibility rules that you should respect).
Ideally asdf
would provide a way to load completion just in time after enabling a specific version. This may either be a dynamic completion (like in the kubectl
case) or loading some static completion from files.
A note: asdf
is really useful when you need multiple version of the same tool; as those versions do not guarantee compatibility between each other, is possible that completion change between different version, so each installed version should ideally have it's completion loaded when enabled.
Do you think this would be possible? I'm willing to submit a PR but I'd like to discuss how to implement this first.
(BTW thank you for the awesome project 💪)
Here is a quick hack that I came up with:
#!/usr/bin/env bash
_asdf_complete_kubectl() {
currentVersion="$(asdf current kubectl)"
if [[ ${_ASDF_COMPLETE_KUBECTL_VER} = "${currentVersion}" ]]
then
__start_kubectl "${@}"
else
source <(kubectl completion bash)
complete -F _asdf_complete_kubectl kubectl
_ASDF_COMPLETE_KUBECTL_VER="${currentVersion}"
fi
}
complete -F _asdf_complete_kubectl kubectl
This registers _asdf_complete_kubectl
as completion handler for kubectl
.
That function loads the kubectl
completions and "remembers" that it already loaded those for the currently active kubectl
verison.
If the version changes (e.g., by calling asdf
, or because of a .tool-versions
file), then it will load the completions of the now active version.
I did not know how to integrate this into asdf
; in particular, it seems that completions are not handled at all (i.e., you need to source them yourself).
Furthermore, this snippet is not perfect, because the actual completion of kubectl is not only determined by __start_kubectl
:
> source <(kubectl completion bash)
> complete -p kubectl
complete -o default -F __start_kubectl kubectl
With the limited time I spent on this, I was not able to figure out how to call the original completion, but in this particular case calling __start_kubectl
seems good enough to me.
I am curious if this could be generalized for other asdf plugins (e.g., helm
), and if something similar could be used for plugins with static completions (e.g., ripgrep
).
Maybe someone with more knowledge about asdf could chime in :)
FWIW, a general version of this is not so hard (requires Bash 4.3 for the variable references):
# Run a completion, loads it lazily if not already done
__asdf_complete() {
local pluginName="${1}"
shift
local currentVersion
currentVersion="$(asdf current "${pluginName}")"
local loadedCompletionName="_ASDF_COMPLETE_${pluginName}_VER"
if [[ ${!loadedCompletionName} != "${currentVersion}" ]]
then
_asdf_load_completion
printf -v "${loadedCompletionName}" "%s" "${currentVersion}"
fi
_asdf_complete "${@}"
}
# Completion for kubectl
_asdf_complete_kubectl() {
_asdf_complete() {
__start_kubectl "${@}"
}
_asdf_load_completion() {
source <(kubectl completion bash)
complete -F _asdf_complete_kubectl kubectl
}
__asdf_complete "kubectl" "${@}"
}
complete -F _asdf_complete_kubectl kubectl
# Completion for helm
_asdf_complete_helm() {
_asdf_complete() {
__start_helm "${@}"
}
_asdf_load_completion() {
source <(helm completion bash)
complete -F _asdf_complete_helm helm
}
__asdf_complete "helm" "${@}"
}
complete -F _asdf_complete_helm helm
Note that asdf current foo
will not only print the version, but also where it was defined (e.g., which .tool-versions
file); as such, it could happen that this will load the same completions more than once, if the same version is defined in different .tool-versions
files.
While this could be fixed by removing everything except the version, I feel like there should be something in asdf
that only returns the version without the "source".
This obviously also works for static completions, e.g.,
# Completion for rg
_asdf_complete_ripgrep() {
_asdf_complete() {
_rg "${@}"
}
_asdf_load_completion() {
local completionFile
completionFile="$(asdf where ripgrep)/complete/rg.bash"
[[ -f ${completionFile} ]] && source "${completionFile}"
complete -F _asdf_complete_ripgrep rg
}
__asdf_complete "ripgrep" "${@}"
}
complete -F _asdf_complete_ripgrep rg
It seems though that static completions might not be at the same path for different versions of a plugin or even missing for earlier versions.
Adding to this, manually sourcing completion files in your .bashrc
(or equivalent file for other shells) - wether it's through kubectl-style indirections or static files provided by the tool - is not only inconvenient, it also adds massive overhead. Right now I'm running something along the lines of _<tool>_dir="$(asdf where <tool>)"
along with logic that has to be customized on a per-tool basis to source completions.
Lazily loading completions for kubectl-style tools helps slightly, but even asdf where <tool>
itself is slow to the point of being an actual annoyance once you do this for more than one or two tools, though by the looks of it that's not easily fixed. I feel like drafting a standard for plugin authors to follow with regards to the location of completion files would be a great first step towards cleaning this up.
In the case of kubectl for example, you could have an optional bin/completions
script which could make use of the lazy-loading mentioned above, leaving the implementation details to the plugin author. For static completions provided by the tool it could perhaps be as simple as a symlink.
Thoughts?
I would love to contribute on this feature. If no one working on this.
@kamontat Go ahead! This is a very useful feature
@hyperupcall Do we have design review yet?
Sorry for late reply. I didn't saw your message.
@kamontat There is no design review besides just the comments in this issue. I also have some extra notes below.
When implementing, I think it is important to consider:
For [1], it might be worth looking into supporting a new file for plugins to have: bin/completions
(along with bin/install
, etc.). Only the plugins know how completions are shown for their specific tool, so this file would be a common way to expose that.
For [2], I'd say it's okay for an initial implementation to only support one shell (perhaps Bash). More shells can be supported in future PRs.
[3] isn't as important, because that logic should be placed in each bin/completions
file. For example, NodeJS added some --bash-completion
like flag relatively recently. You wouldn't want to try to invoke that on an earlier version. So these checks are important
I wrote [4] and [5] for similar reasons. This feature will absolutely kill performance, once implemented. So, an initial implementation will either be opt-in, to allow for incremental improvements without disrupting existing workflows, and/or cache like crazy so things don't slow down even more.
Describe the solution you'd like I recently discovered asdf, and installed it hoping I could manage all my various hand-installed binaries (especially everything I use for Kubernetes). Most of the Kubernetes tools are much less useful without completions, as you often have to look up a pod name and then secondly perform some action on them, in two separate commands. With command completions, I can simply autocomplete the pod names, which saves me running an entire command.
A lot of kubernetes tools, such as
kubectl
andhelm
all include completions directly in the binary, but they have to be loaded like this (in zsh):I'm aware that many plugins provide completions in different ways, but pretty much all Kubernetes tools provide them like this.
Would it be possible for asdf to somehow enable the loading of completions from each plugin, such that a plugin author can include some configuration that tells asdf how to load completions.
Describe similar asdf features and why they are not sufficient asdf already supplies completions for itself, but that doesn't help me look up pods via kubectl.
Describe workarounds you've considered The simplest workaround would be to simply add all the completions in my .zshrc, like I've always done, but it would be nice if asdf automatically installed and set up the completions.