3

Given two commands:

  • command1 - which has completions already set up for it
  • commmand2- which is a wrapper function, that in the end calls command1 ARG

How do I make command2 complete in the same manner as command1 ARG1 would without writing a custom completion for command1?

Here's an example:

alias command1="git ls-files"

command2() {
  echo "I'm a wrapper for git ls-files" >&2
  git ls-files $@
}

One can do compdef command2=command1 - but that will make command2 complete the same way as git would and not like git ls-files.

Edit: I'm looking for a broad and general solution that'd also work with commands that do not define separate completion functions such as git has. For these you can do what Marlon Richert suggested below.

Here's a better example:

alias command1="kubectl get pod"

command2() {
  echo "I'm a wrapper for kubectl get pod" >&2
  kubectl get pod $@
}
pschmitt
  • 93
  • 1
  • 6

1 Answers1

4

Do this to find out the name of the function that needs to be called:

% git ls-files ^Xh  # That is, press Ctrl-X, then H.
tags in context :completion::complete:git-ls-files::
    argument-rest options  (_arguments _git-ls-files _git)
tags in context :completion::complete:git-ls-files:argument-rest:
    globbed-files  (_files _arguments _git-ls-files _git)
tags in context :completion::complete:git::
    argument-rest  (_arguments _git)

As you can see, it's _git-ls-files.

Then, discard the leading _ and use the remainder as the $service argument to compdef:

compdef _git command2=git-ls-files

Now it works correctly:

% command2 ^Xh
tags in context :completion::complete:command2::
    argument-rest options  (_arguments _git-ls-files _git)
tags in context :completion::complete:command2:argument-rest:
    globbed-files  (_files _arguments _git-ls-files _git)

Update

For your kubectl example, things are slightly less easy, because its completion is not Zsh-native. Instead, it's just a thin Zsh wrapper around a Bash completion function. In this case, you will have to write your own completion function, but thankfully, it's going to be just a mercifully short one:

_command2 () {
  # Fake having `kubectl get pod` on the line instead of `command2`.
  local -a words=( kubectl get pod $words[2,-1] )
  local -i CURRENT=$(( CURRENT + 2 ))

  # Restart completion with our new, simulated command line.
  _normal
}
compdef _command2 command2

Done!

Marlon Richert
  • 5,250
  • 1
  • 18
  • 27
  • Thanks. While this does work for my particular example this isn't quite general enough. This only works if there actually is a completion function defined specifically for the subcommand. Most comps I've seen are not implemented in that way. – pschmitt Mar 30 '21 at 09:42
  • I've updated my post to with a second example ;) – pschmitt Mar 30 '21 at 10:01
  • I saw it. I think I'll mark this question answered even though I explicitly specified: > without writing a custom completion but I think there's just isn't a perfectly elegant way to do what I want w/ zsh. I may end up implementing a wrapper function that auto-creates comps in a similar fashion to what you suggested for the kubectl example. Thanks for taking the time to answer, I truly appreciate it. – pschmitt Mar 30 '21 at 15:23
  • Your comment made me realize my second answer can be even shorter. I edited it; have another look. It might help you make a start on that generic wrapper function. :) – Marlon Richert Mar 30 '21 at 18:38
  • It might be cleaner to replace the `eval` line with `_normal`, which just restarts completion with the new `$words` and `$CURRENT`. – yut23 Jul 08 '22 at 00:32