I have zsh completion file for custom script foo.

It should complete regular files in $HOME/foo :

#compdef foo

_arguments -S : \
    '-i' \
    '1:thing:_path_files -W $HOME/foo -g "*(.)"'

I need to modify the completion as follows:

Add optional parameter --dir.

When I type foo --dir<TAB>, it should offer completion for directories in $HOME/foo

foo --dir=<TAB>

And also, the file completion path (which is $HOME/foo by default) should change to $HOME/foo/my_dir, where my_dir is the name provided as --dir=my_dir.


Basically, the completion rule is supposed to have 2 modes of operation:

  1. when I type foo -<TAB>, it is supposed to offer me optional parameters (-d -w -l -i). Then, depending on the optional parameter chosen:

1a) foo -d <TAB> completes only directories in the path

1b) foo -w / -l / -i <TAB> completes regular files in the path

this works as desired.

  1. when I type foo <TAB>, the completion rule is supposed to offer regular files in the path. Currently, this does not work as desired. It still offers me the optional parameters, and I have to chose one, to then have directories/files completed.

When I type foo <TAB> (without the -), it only offers me the option parameters. Only after I have chosen one of the optional parameters, does my completion rule offer directories/files in the path.

How can I fix my script, so that it works without any optional parameters, and in such case simply completes regular files from the path ?

Here is the completion rule:

#compdef foo

local -A opt_args

_arguments -S -C \
    '-i[select file]:thing_file:_path_files -W $HOME/manuals/${opt_args[-d]} -g "*(.)"' \
    '-l[select file]:thing_file:_path_files -W $HOME/manuals/${opt_args[-d]} -g "*(.)"' \
    '-w[select file]:thing_file:_path_files -W $HOME/manuals/${opt_args[-d]} -g "*(.)"' \
    '-d[select directory]:directory_file:_path_files -/ -W $HOME/manuals' \
     && return 0

And one last small issue. The completion rule expects (completes) only one optional parameter.

When I type foo -i -<TAB> it does not offer other remaining optional parameters. These parameters should not be mutually exclusive. It should be possible to use:

foo -i -l -w <TAB>

1 Answer 1


Like that:

#compdef foo

local -A opt_args

_arguments -S -C \
    '-i[select file]:thing_file:_path_files -W $HOME/foo/${opt_args[-d]} -g "*(.)"' \
    '-d[select directory]:directory_file:_path_files -/ -W $HOME/foo' \
     && return 0

To explain how it works functionally:

Create ~/foo:

mkdir ~/foo

Create 10 directories inside ~/foo:

mkdir ~/foo/DIR{1..10}

Create a file in the 4th directory:

$ touch ~/foo/DIR4/FILE4

And a file in ~/foo:

touch ~/foo/FILE-IN-FOO


foo -d

and press Tab. It shows:

$foo -d DIR1/
DIR1/   DIR10/  DIR2/   DIR3/   DIR4/   DIR5/   DIR6/   DIR7/   DIR8/   DIR9/

Only directories are shown so no FILE-IN-FOO.

Select DIR4 and add -i:

foo -d DIR4 -i

and press Tab. It shows:

foo -d DIR4 -i FILE4
  • I have small problem with the completion rule. I have started a bounty. Would you please look, I have explained it in the UPDATE. Thank you. Commented May 18, 2022 at 4:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .