3

I have a function called load_pg, which is defined like this:

load_pg () {
    pg_restore --verbose --clean --no-acl --no-owner -h localhost -U $1 -d $2 $3
}

And I'm trying to autocomplete each parameter, with the following code:

#compdef load_pg
_arguments -s \
  "1::_ldpguser" \
  "2::_ldpgdb" \
  "3::_ldpgfile"

_ldpguser () {
  compadd $USER
}

_ldpgdb () {
  compadd $(cat config/database.yml | grep -i database | awk '{print $2}')
}

_ldpgfile () {
  compadd $(ls *.dump*)
}

Unfortunately, nothing happens when I press TAB. What am I doing wrong? I tried using code from the following answer

  • What's the shell you use? – dchirikov Oct 9 '13 at 10:35
  • I'm using z shell – Geo Oct 9 '13 at 10:44
4

First hurdle: the syntax of an argument to _arguments is n:message:action where message is not allowed to be empty. If you add a message, you'll see some progress:

_arguments -s \
  "1:username:_ldpguser" \
  "2:database:_ldpgdb" \
  "3:dump file:_ldpgfile"

The next hurdle is that the auxiliary functions are defined after _arguments runs, so the very first time you complete the arguments you'll get an error message complaining that one of the functions doesn't exist. Define the functions before using them. Better, make the definition of the _load_pg function explicit and call it at the end of the autoloaded file. This looks clumsy, but it's the way most multi-function completion functions shipped with zsh are written.

#compdef load_pg

_ldpguser () {
  compadd $USER
}

_ldpgdb () {
  compadd $(cat config/database.yml | grep -i database | awk '{print $2}')
}

_ldpgfile () {
  compadd $(ls *.dump*)
}

_load_pg () {
  _arguments -s \
    "1:user:_ldpguser" \
    "2:database:_ldpgdb" \
    "3:dump file:_ldpgfile"
}

_load_pg "$@"

This gives you the core of the function. You'll then want to clean up the individual functions. At a minimum:

  • compadd $USER seems pointless: if you only ever want to call the function with your user name, why not build it in? To complete user names, call _users.
  • Command substitution substitutes at all whitespace. To split lines, use the @ and f parameter expansion flags. You should also probably make your grep command more precise, and you may be able to combine it with grep.
  • When you call compadd in an action from _arguments, pass it the contextual options in $expl.
  • You may want to provide a description for completions with _describe.
  • $(ls *.dump*) is a convoluted way to write “*.dump*, and if there is no match, do something that depends on current shell settings”. Don't parse the output of ls, mmmkay? You could use N glob qualifier to have an empty list of completions when there is no match. However, you should instead call _files which takes care of completing files in subdirectories, among other niceties.
#compdef load_pg

_ldpgdb () {
  compadd $expl[@] -- "${(@f)$(<config/database.yml grep -i database | awk '{print $2}')}"
}

_load_pg () {
  _arguments -s \
    '1:user:_users' \
    '2:database:_ldpgdb' \
    '3:dump file:_files -g "*.dump*"'
}

_load_pg "$@"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.