1

I have a function mycmd to launch a program that I wrote. The program needs the first argument to be foo, ssh or ls. The second argument depends on the first argument as follows,

  1. foo -> No second argument
  2. ssh -> Something to ssh to
  3. ls -> A file

I want to write zsh autocomplete function for mycmd which suggest the second argument depending on the first argument. In the simplest form, I know that I can do the following for the first argument

_mycmd() {
    compadd foo ssh ls
}
compdef _mycmd mycmd

I have a hard time understanding what to do for the second argument from here. How do I use _ssh autocomplete for ssh argument and _ls autocomplete for ls argument? (And nothing for foo as well)

Teshan Shanuka J
  • 1,448
  • 2
  • 17
  • 31

1 Answers1

1

To inspect the current command line, it could be used $words and $CURRENT that are the completion special parameters.

CURRENT
This is the number of the current word, i.e. the word the cursor is currently on in the words array.
...
words
This array contains the words present on the command line curently being edited.

--- zshcompwid(1), completion special parameters, zshcompwid - zsh completion widgets

The completion function could modify $words and $CURRENT (and/or other variables) and then start the entire completion system based with its modified command line. For example:

$ mycmd ls -al<TAB>               ;# This is the input, and
;# $words == ("mycmd" "ls" "-al") ;# original value for $words.
;# $words=("ls" "-al")            ;# We could update $words for making zsh
;# $ ls -al<TAB>                  ;# to start the completion system with
                                  ;# its modified command line.
                                  ;# Finally, _ls would be called.

The utility function _normal could be used.

_normal
...
A second use is to reexamine the command line specified by the $words array and the $CURRENT parameter after those have been modified.

-- zshcompsys(1), utility function, _normal

_mycmd could be listed below:

_mycmd () {
  if ((CURRENT == 2)); then
    compadd foo ssh ls
  elif ((CURRENT > 2)); then
    case "$words[2]" in
      (ssh|ls)
        shift words
        ((CURRENT--))
        _normal -p mycmd
        ;;
      (foo)
        _nothing
        ;;
      (*)
        _message "mycmd: invalid subcommand or arguments"
        ;;
    esac
  fi
  return $?
}

or even, more use of the completion builtin/utility functions like below:

_mycmd () {
  local curcontext="${curcontext}" state context line
  local -A opt_args
  _arguments '*:: :->subcmd'
  if [[ "$state" == "subcmd" ]]; then
    if ((CURRENT == 1)); then
      _describe -t mycmd-subcmd "mycmd command" '(foo ssh ls)'
    else
      curcontext="${curcontext%:*:*}:mycmd-$words[1]:"
      case "$words[1]" in
        (ssh|ls)
          compset -n 1
          _normal -p $service
        ;;
        (foo)
          _nothing
        ;;
        (*)
          _message "mycmd: invalid subcommand or arguments"
        ;;
      esac
    fi
  fi
  return $?
}
hchbaw
  • 4,894
  • 18
  • 16