Vladimir Panteleev
Vladimir Panteleev

Reputation: 25187

Complete a command (with arguments) like another command (with other arguments)

compdef cmd1=service can be used to define a completion alias, however, that works only when the arguments are going to be the same.

For example, consider a helper script which rewrites some arguments before executing another command:

| What is typed | What is executed           |
|---------------+----------------------------|
| s             | systemctl                  |
| s q           | systemctl status           |
| s q foo       | systemctl status foo       |
| s j foo       | journalctl --unit foo      |
| s r foo       | sudo systemctl restart foo |

We can ask the script to print the arguments it would execute, so e.g. PRINT_ONLY=1 s would print just systemctl.

Assuming completion is already set up for systemctl / journalctl / sudo, how would one define a zsh completion for such a script? Rather than redundantly reimplementing completion for those commands, how to implement completion for s such that the completion system is invoked with a transformed command -- i.e. something like function _s() { get_completions $(PRINT_ONLY=1 s "$@") ; }?

Upvotes: 3

Views: 905

Answers (1)

yut23
yut23

Reputation: 3064

This should go in a file named _s somewhere on your $fpath:

#compdef s

local -a orig_command new_words
orig_command=("${words[@]}")
if [[ $words[-1] == '' ]]; then
  # remove an empty word at the end, which the completion system cares about
  orig_command[-1]=()
fi
# split the rewritten command into words using the shell parser
new_words=("${(z)$(PRINT_ONLY=1 "${orig_command[@]}")}")
if [[ $words[-1] == '' ]]; then
  # restore the empty word if we removed it
  new_words+=('')
fi
# update the cursor position
CURRENT=$(( CURRENT - $#words + $#new_words ))
words=("${new_words[@]}")
# restart completion with the rewritten command
_normal

Note: this doesn't do any error handling and just assumes that any unknown arguments will be passed to the default command (e.g. s start foo -> systemctl start foo). If that's not the case, let me know how s behaves in those cases and I can update the completion script.

Upvotes: 2

Related Questions