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

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.