From 6243d6546301855c6fc71680e2bf6976f064aee1 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Thu, 11 Jul 2024 05:58:34 +0700 Subject: [PATCH] fix: Only generate stubs if has subcommands --- clap_complete/src/shells/fish.rs | 22 +++------- .../tests/snapshots/subcommand_last.fish | 44 +++++++++++++++---- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/clap_complete/src/shells/fish.rs b/clap_complete/src/shells/fish.rs index 3fdf8b73..25404c6b 100644 --- a/clap_complete/src/shells/fish.rs +++ b/clap_complete/src/shells/fish.rs @@ -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}; @@ -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"; diff --git a/clap_complete/tests/snapshots/subcommand_last.fish b/clap_complete/tests/snapshots/subcommand_last.fish index c4cdd440..4f5876ea 100644 --- a/clap_complete/tests/snapshots/subcommand_last.fish +++ b/clap_complete/tests/snapshots/subcommand_last.fish @@ -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)'