3

I am trying to write some completions for an in-house tool. We'll call it thetool.

Lots of the commands to thetool do not take a 'file' as an argument. I thought that --no-files and/or --exclusive would do this for me but I don't seem to be able to get it to work.

In other words how can I write completions so that the following command does show files in the tab completion

$ thetool file-command *hit-tab*

while the following command does NOT show files in the tab completion

$ thetool non-file-command *hit-tab*

Let's try for a toy example:

$ function thetool
    echo $argv
  end
$ complete --command thetool --no-files --condition "not __fish_seen_subcommand_from file-command non-file-command" --arguments "file-command" --description "file-command"
$ complete --command thetool --no-files --condition "not __fish_seen_subcommand_from file-command non-file-command" --arguments "non-file-command" --description "non-file-command"
$ complete --command thetool
complete --no-files thetool -d non-file-command -a non-file-command -n 'not __fish_seen_subcommand_from file-command non-file-command'
complete --no-files thetool -d file-command -a file-command -n 'not __fish_seen_subcommand_from file-command non-file-command'

now try the completions out:

$ thetool *hit-tab*
file-command  (file-command)  non-file-command  (non-file-command)

Looks good so far...

$ thetool non-file-command *hit-tab*
bin/                  dev.yml       Judgment.lock  README.md              TODO.md           
completion_test.fish  exe/          lib/           shipit.production.yml  vendor/           
completion_test.zsh   Gemfile       linters/       sorbet/                zsh_completions.sh
coverage/             Gemfile.lock  Rakefile       test/                  

SEE! What are all those file suggestions doing there?!

Let's try again:

$ complete --command thetool --force-files --condition "not __fish_seen_subcommand_from file-command non-file-command" --arguments "file-command" --erase
$ complete --command thetool --force-files --condition "not __fish_seen_subcommand_from file-command non-file-command" --arguments "file-command"
$ complete --command thetool --no-files --condition "not __fish_seen_subcommand_from file-command non-file-command" --arguments "non-file-command"
$ complete --command thetool
complete --no-files thetool -a non-file-command -n 'not __fish_seen_subcommand_from file-command non-file-command'
complete --force-files thetool -a file-command -n 'not __fish_seen_subcommand_from file-command non-file-command'
$ thetool *hit-tab*
bin/                  dev.yml       Gemfile.lock   non-file-command       sorbet/  zsh_completions.sh
completion_test.fish  exe/          Judgment.lock  Rakefile               test/    
completion_test.zsh   file-command  lib/           README.md              TODO.md  
coverage/             Gemfile       linters/       shipit.production.yml  vendor/  

arrrg can someone help me understand what is going on and what I'm doing wrong?

1 Answer 1

2

You have no call that ever tells fish to disable files if it has seen non-file-command.

Let's go through them:

complete --command thetool --no-files --condition "not __fish_seen_subcommand_from file-command non-file-command" --arguments "file-command" --description "file-command"

fish has seen non-file-command, the condition is false.

complete --command thetool --no-files --condition "not __fish_seen_subcommand_from file-command non-file-command" --arguments "non-file-command" --description "non-file-command"

fish has seen non-file-command, the condition is false.

complete --command thetool

This does nothing.

complete --no-files thetool -d non-file-command -a non-file-command -n 'not __fish_seen_subcommand_from file-command non-file-command'

fish has seen non-file-command, the condition is false.

complete --no-files thetool -d file-command -a file-command -n 'not __fish_seen_subcommand_from file-command non-file-command'

fish has seen non-file-command, the condition is false.

No condition where we'd disable files is true, so we default to enabling them. There is nothing that tells it to disable files if non-file-command was given.

Add

complete --command thetool --no-files --condition " __fish_seen_subcommand_from non-file-command"

and it'll work.

Or, if you have a lot of things that don't take files and just a few that do, you can add

complete --command thetool --no-files

which will disable files always (it has no condition so it's always true, as long as you're completing thetool), and then force-files for those cases that do, e.g.

complete --command thetool --condition "__fish_seen_subcommand_from file-command" --force-files
4
  • Thank you! I think this might sort me out. I'll report back after I've given this a try in the use cases for my completion use-case. Jul 29, 2022 at 12:02
  • This seems to have been the piece I was missing. I think I understand now though. As you say, I needed to give it the condition for which to apply the --no-files or --force-files My command doesn't have a lot of commands that take files, and so I've chosen the second route you proposed which turns on --no-files by default for everything under thetool and in specific cases where I know I need files I've used --force-files with a specific --condition "__fish_seen_subcommand_from file-command". Jul 29, 2022 at 12:26
  • A related question that we should maybe sneak in here - is there a way to tell fishs built-in file completion to only allow certain files by checking their extension? Jul 29, 2022 at 12:27
  • 1
    No. Extension checking in general is fraught with problems, and useful much less than you think. So you'll have to write your own if it's really what you want. A few globs here and there would get you most of the way.
    – faho
    Jul 29, 2022 at 13:58

You must log in to answer this question.

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