Skip to content

Commit

Permalink
fix: Only generate stubs if has subcommands
Browse files Browse the repository at this point in the history
  • Loading branch information
tesuji committed Jul 10, 2024
1 parent 76b9c46 commit 6243d65
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 26 deletions.
22 changes: 5 additions & 17 deletions clap_complete/src/shells/fish.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::io::Write;

use clap::{builder, Arg, ArgAction, Command, ValueHint};
use clap::{builder, Arg, Command, ValueHint};

use crate::generator::{utils, Generator};

Expand All @@ -20,25 +20,13 @@ impl Generator for Fish {
.get_bin_name()
.expect("crate::generate should have set the bin_name");

// If there is any regular flags, we may have complicated cases, e.g. `git --git-dir somedir status`. Using normal
// `__fish_seen_subcommand_from` won't help us find out the real subcommand is `status`, and not `somedir`.
// However, we prefer to fallback to the old behavior when there are no regular flags. `-h` and `-v` is not
// a regular flag and it behaves like a command. E.g., `rustup --version toolchain` is not a valid command line.
let has_global_flags = cmd.get_arguments().any(|a| {
!a.is_positional()
&& !matches!(
a.get_action(),
ArgAction::Help
| ArgAction::HelpShort
| ArgAction::HelpLong
| ArgAction::Version
)
});

let name = escape_name(bin_name);
let mut needs_fn_name = &format!("__fish_{name}_needs_command")[..];
let mut using_fn_name = &format!("__fish_{name}_using_subcommand")[..];
if has_global_flags && cmd.has_subcommands() {
// Given `git --git-dir somedir status`, using `__fish_seen_subcommand_from` won't help us
// find out `status` is the real subcommand, and not `somedir`. However, when there are no subcommands,
// there is no need to use our custom stubs.
if cmd.has_subcommands() {
gen_subcommand_helpers(&name, cmd, buf, needs_fn_name, using_fn_name);
} else {
needs_fn_name = "__fish_use_subcommand";
Expand Down
44 changes: 35 additions & 9 deletions clap_complete/tests/snapshots/subcommand_last.fish
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
complete -c my-app -n "__fish_use_subcommand" -s h -l help -d 'Print help'
complete -c my-app -n "__fish_use_subcommand" -a "foo"
complete -c my-app -n "__fish_use_subcommand" -a "bar"
complete -c my-app -n "__fish_use_subcommand" -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c my-app -n "__fish_seen_subcommand_from foo" -s h -l help -d 'Print help'
complete -c my-app -n "__fish_seen_subcommand_from bar" -s h -l help -d 'Print help'
complete -c my-app -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from foo bar help" -f -a "foo"
complete -c my-app -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from foo bar help" -f -a "bar"
complete -c my-app -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from foo bar help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'
# Print an optspec for argparse to handle cmd's options that are independent of any subcommand.
function __fish_my_app_global_optspecs
string join \n h/help
end

function __fish_my_app_needs_command
# Figure out if the current invocation already has a command.
set -l cmd (commandline -opc)
set -e cmd[1]
argparse -s (__fish_my_app_global_optspecs) -- $cmd 2>/dev/null
or return
if set -q argv[1]
# Also print the command, so this can be used to figure out what it is.
echo $argv[1]
return 1
end
return 0
end

function __fish_my_app_using_subcommand
set -l cmd (__fish_my_app_needs_command)
test -z "$cmd"
and return 1
contains -- $cmd[1] $argv
end

complete -c my-app -n "__fish_my_app_needs_command" -s h -l help -d 'Print help'
complete -c my-app -n "__fish_my_app_needs_command" -a "foo"
complete -c my-app -n "__fish_my_app_needs_command" -a "bar"
complete -c my-app -n "__fish_my_app_needs_command" -a "help" -d 'Print this message or the help of the given subcommand(s)'
complete -c my-app -n "__fish_my_app_using_subcommand foo" -s h -l help -d 'Print help'
complete -c my-app -n "__fish_my_app_using_subcommand bar" -s h -l help -d 'Print help'
complete -c my-app -n "__fish_my_app_using_subcommand help; and not __fish_seen_subcommand_from foo bar help" -f -a "foo"
complete -c my-app -n "__fish_my_app_using_subcommand help; and not __fish_seen_subcommand_from foo bar help" -f -a "bar"
complete -c my-app -n "__fish_my_app_using_subcommand help; and not __fish_seen_subcommand_from foo bar help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)'

0 comments on commit 6243d65

Please sign in to comment.