diff --git a/clap_complete/examples/test.rs b/clap_complete/examples/test.rs index 8129fb03..981f8334 100644 --- a/clap_complete/examples/test.rs +++ b/clap_complete/examples/test.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "unstable-dynamic")] +use clap::{FromArgMatches, Subcommand}; use clap_complete::{generate, Generator, Shell}; fn main() { @@ -6,9 +8,18 @@ fn main() { let mut cmd = cli(); eprintln!("Generating completion file for {generator}..."); print_completions(*generator, &mut cmd); - } else { - println!("{:?}", matches); + return; } + + #[cfg(feature = "unstable-dynamic")] + if let Ok(completions) = + clap_complete::dynamic::shells::CompleteCommand::from_arg_matches(&matches) + { + completions.complete(&mut cli()); + return; + }; + + println!("{:?}", matches); } fn print_completions(gen: G, cmd: &mut clap::Command) { @@ -16,7 +27,7 @@ fn print_completions(gen: G, cmd: &mut clap::Command) { } fn cli() -> clap::Command { - clap::Command::new("test") + let cli = clap::Command::new("test") .version("3.0") .propagate_version(true) .args([ @@ -27,6 +38,7 @@ fn cli() -> clap::Command { .help("everywhere"), clap::Arg::new("generate") .long("generate") + .value_name("SHELL") .value_parser(clap::value_parser!(Shell)) .help("generate"), ]) @@ -173,5 +185,8 @@ fn cli() -> clap::Command { .long("email") .value_hint(clap::ValueHint::EmailAddress), ]), - ]) + ]); + #[cfg(feature = "unstable-dynamic")] + let cli = clap_complete::dynamic::shells::CompleteCommand::augment_subcommands(cli); + cli } diff --git a/clap_complete/tests/snapshots/home/static/test/bash/.bashrc b/clap_complete/tests/snapshots/home/static/test/bash/.bashrc index dcb08049..01e95077 100644 --- a/clap_complete/tests/snapshots/home/static/test/bash/.bashrc +++ b/clap_complete/tests/snapshots/home/static/test/bash/.bashrc @@ -20,6 +20,9 @@ _test() { test,alias) cmd="test__alias" ;; + test,complete) + cmd="test__complete" + ;; test,help) cmd="test__help" ;; @@ -44,6 +47,9 @@ _test() { test__help,alias) cmd="test__help__alias" ;; + test__help,complete) + cmd="test__help__complete" + ;; test__help,help) cmd="test__help__help" ;; @@ -153,7 +159,7 @@ _test() { case "${cmd}" in test) - opts="-h -V --global --generate --help --version action quote value pacman last alias hint help" + opts="-h -V --global --generate --help --version action quote value pacman last alias hint complete help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -222,8 +228,30 @@ _test() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + test__complete) + opts="-h -V --shell --register --global --help --version [COMP_WORDS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --shell) + COMPREPLY=($(compgen -W "bash" -- "${cur}")) + return 0 + ;; + --register) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; test__help) - opts="action quote value pacman last alias hint help" + opts="action quote value pacman last alias hint complete help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -264,6 +292,20 @@ _test() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + test__help__complete) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; test__help__help) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then diff --git a/clap_complete/tests/snapshots/home/static/test/elvish/elvish/rc.elv b/clap_complete/tests/snapshots/home/static/test/elvish/elvish/rc.elv index 7b2e8bac..e464910b 100644 --- a/clap_complete/tests/snapshots/home/static/test/elvish/elvish/rc.elv +++ b/clap_complete/tests/snapshots/home/static/test/elvish/elvish/rc.elv @@ -33,6 +33,7 @@ set edit:completion:arg-completer[test] = {|@words| cand last 'last' cand alias 'alias' cand hint 'hint' + cand complete 'Register shell completions for this program' cand help 'Print this message or the help of the given subcommand(s)' } &'test;action'= { @@ -225,6 +226,15 @@ set edit:completion:arg-completer[test] = {|@words| cand -V 'Print version' cand --version 'Print version' } + &'test;complete'= { + cand --shell 'Specify shell to complete for' + cand --register 'Path to write completion-registration to' + cand --global 'everywhere' + cand -h 'Print help (see more with ''--help'')' + cand --help 'Print help (see more with ''--help'')' + cand -V 'Print version' + cand --version 'Print version' + } &'test;help'= { cand action 'action' cand quote 'quote' @@ -233,6 +243,7 @@ set edit:completion:arg-completer[test] = {|@words| cand last 'last' cand alias 'alias' cand hint 'hint' + cand complete 'Register shell completions for this program' cand help 'Print this message or the help of the given subcommand(s)' } &'test;help;action'= { @@ -273,6 +284,8 @@ set edit:completion:arg-completer[test] = {|@words| } &'test;help;hint'= { } + &'test;help;complete'= { + } &'test;help;help'= { } ] diff --git a/clap_complete/tests/snapshots/home/static/test/fish/fish/completions/test.fish b/clap_complete/tests/snapshots/home/static/test/fish/fish/completions/test.fish index 1059fd8c..a1c5714b 100644 --- a/clap_complete/tests/snapshots/home/static/test/fish/fish/completions/test.fish +++ b/clap_complete/tests/snapshots/home/static/test/fish/fish/completions/test.fish @@ -9,6 +9,7 @@ complete -c test -n "__fish_use_subcommand" -f -a "pacman" complete -c test -n "__fish_use_subcommand" -f -a "last" complete -c test -n "__fish_use_subcommand" -f -a "alias" complete -c test -n "__fish_use_subcommand" -f -a "hint" +complete -c test -n "__fish_use_subcommand" -f -a "complete" -d 'Register shell completions for this program' complete -c test -n "__fish_use_subcommand" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c test -n "__fish_seen_subcommand_from action" -l set -d 'value' -r complete -c test -n "__fish_seen_subcommand_from action" -l choice -d 'enum' -r -f -a "{first ,second }" @@ -103,14 +104,20 @@ complete -c test -n "__fish_seen_subcommand_from hint" -l email -r -f complete -c test -n "__fish_seen_subcommand_from hint" -l global -d 'everywhere' complete -c test -n "__fish_seen_subcommand_from hint" -s h -l help -d 'Print help' complete -c test -n "__fish_seen_subcommand_from hint" -s V -l version -d 'Print version' -complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from help" -f -a "action" -complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from help" -f -a "quote" -complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from help" -f -a "value" -complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from help" -f -a "pacman" -complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from help" -f -a "last" -complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from help" -f -a "alias" -complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from help" -f -a "hint" -complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c test -n "__fish_seen_subcommand_from complete" -l shell -d 'Specify shell to complete for' -r -f -a "{bash }" +complete -c test -n "__fish_seen_subcommand_from complete" -l register -d 'Path to write completion-registration to' -r -F +complete -c test -n "__fish_seen_subcommand_from complete" -l global -d 'everywhere' +complete -c test -n "__fish_seen_subcommand_from complete" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c test -n "__fish_seen_subcommand_from complete" -s V -l version -d 'Print version' +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "action" +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "quote" +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "value" +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "pacman" +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "last" +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "alias" +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "hint" +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "complete" -d 'Register shell completions for this program' +complete -c test -n "__fish_seen_subcommand_from help; and not __fish_seen_subcommand_from action; and not __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from value; and not __fish_seen_subcommand_from pacman; and not __fish_seen_subcommand_from last; and not __fish_seen_subcommand_from alias; and not __fish_seen_subcommand_from hint; and not __fish_seen_subcommand_from complete; and not __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c test -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from cmd-single-quotes; and not __fish_seen_subcommand_from cmd-double-quotes; and not __fish_seen_subcommand_from cmd-backticks; and not __fish_seen_subcommand_from cmd-backslash; and not __fish_seen_subcommand_from cmd-brackets; and not __fish_seen_subcommand_from cmd-expansions" -f -a "cmd-single-quotes" -d 'Can be \'always\', \'auto\', or \'never\'' complete -c test -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from cmd-single-quotes; and not __fish_seen_subcommand_from cmd-double-quotes; and not __fish_seen_subcommand_from cmd-backticks; and not __fish_seen_subcommand_from cmd-backslash; and not __fish_seen_subcommand_from cmd-brackets; and not __fish_seen_subcommand_from cmd-expansions" -f -a "cmd-double-quotes" -d 'Can be "always", "auto", or "never"' complete -c test -n "__fish_seen_subcommand_from help; and __fish_seen_subcommand_from quote; and not __fish_seen_subcommand_from cmd-single-quotes; and not __fish_seen_subcommand_from cmd-double-quotes; and not __fish_seen_subcommand_from cmd-backticks; and not __fish_seen_subcommand_from cmd-backslash; and not __fish_seen_subcommand_from cmd-brackets; and not __fish_seen_subcommand_from cmd-expansions" -f -a "cmd-backticks" -d 'For more information see `echo test`' diff --git a/clap_complete/tests/snapshots/home/static/test/zsh/zsh/_test b/clap_complete/tests/snapshots/home/static/test/zsh/zsh/_test index dff89475..4e156f38 100644 --- a/clap_complete/tests/snapshots/home/static/test/zsh/zsh/_test +++ b/clap_complete/tests/snapshots/home/static/test/zsh/zsh/_test @@ -15,7 +15,7 @@ _test() { local context curcontext="$curcontext" state line _arguments "${_arguments_options[@]}" \ -'--generate=[generate]: :(bash elvish fish powershell zsh)' \ +'--generate=[generate]:SHELL:(bash elvish fish powershell zsh)' \ '--global[everywhere]' \ '-h[Print help]' \ '--help[Print help]' \ @@ -307,6 +307,18 @@ _arguments "${_arguments_options[@]}" \ '*::command_with_args:_cmdambivalent' \ && ret=0 ;; +(complete) +_arguments "${_arguments_options[@]}" \ +'--shell=[Specify shell to complete for]:SHELL:(bash)' \ +'--register=[Path to write completion-registration to]:REGISTER:_files' \ +'--global[everywhere]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'-V[Print version]' \ +'--version[Print version]' \ +'*::comp_words:' \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" \ ":: :_test__help_commands" \ @@ -403,6 +415,10 @@ _arguments "${_arguments_options[@]}" \ _arguments "${_arguments_options[@]}" \ && ret=0 ;; +(complete) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" \ && ret=0 @@ -426,6 +442,7 @@ _test_commands() { 'last:' \ 'alias:' \ 'hint:' \ +'complete:Register shell completions for this program' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'test commands' commands "$@" @@ -540,6 +557,16 @@ _test__quote__help__cmd-single-quotes_commands() { local commands; commands=() _describe -t commands 'test quote help cmd-single-quotes commands' commands "$@" } +(( $+functions[_test__complete_commands] )) || +_test__complete_commands() { + local commands; commands=() + _describe -t commands 'test complete commands' commands "$@" +} +(( $+functions[_test__help__complete_commands] )) || +_test__help__complete_commands() { + local commands; commands=() + _describe -t commands 'test help complete commands' commands "$@" +} (( $+functions[_test__help_commands] )) || _test__help_commands() { local commands; commands=( @@ -550,6 +577,7 @@ _test__help_commands() { 'last:' \ 'alias:' \ 'hint:' \ +'complete:Register shell completions for this program' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'test help commands' commands "$@" diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index 51e2ebf2..81c2ea91 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -156,8 +156,8 @@ fn complete() { let input = "test \t\t"; let expected = r#"% --h --global --help action value last hint --V --generate --version quote pacman alias help"#; +-h --global --help action value last hint help +-V --generate --version quote pacman alias complete"#; let actual = runtime.complete(input, &term).unwrap(); snapbox::assert_eq(expected, actual); } diff --git a/clap_complete/tests/testsuite/common.rs b/clap_complete/tests/testsuite/common.rs index a26a5464..50a58471 100644 --- a/clap_complete/tests/testsuite/common.rs +++ b/clap_complete/tests/testsuite/common.rs @@ -308,9 +308,17 @@ pub fn register_example(context: &str, name: &str, shell: completest::Shell) { .join(shell_name); println!("Compiling"); let manifest_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml"); - let bin_path = - snapbox::cmd::compile_example(name, ["--manifest-path", manifest_path.to_str().unwrap()]) - .unwrap(); + let bin_path = snapbox::cmd::compile_example( + name, + [ + "--manifest-path", + manifest_path.to_str().unwrap(), + // Unconditionally include to avoid completion file tests failing based on the how + // `cargo test` is invoked + "--features=unstable-dynamic", + ], + ) + .unwrap(); println!("Compiled"); let bin_root = bin_path.parent().unwrap().to_owned(); @@ -353,9 +361,17 @@ pub fn load_runtime( let home = scratch.path().unwrap().to_owned(); println!("Compiling"); let manifest_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml"); - let bin_path = - snapbox::cmd::compile_example(name, ["--manifest-path", manifest_path.to_str().unwrap()]) - .unwrap(); + let bin_path = snapbox::cmd::compile_example( + name, + [ + "--manifest-path", + manifest_path.to_str().unwrap(), + // Unconditionally include to avoid completion file tests failing based on the how + // `cargo test` is invoked + "--features=unstable-dynamic", + ], + ) + .unwrap(); println!("Compiled"); let bin_root = bin_path.parent().unwrap().to_owned(); diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs index 5b24d57b..46be7404 100644 --- a/clap_complete/tests/testsuite/elvish.rs +++ b/clap_complete/tests/testsuite/elvish.rs @@ -146,6 +146,7 @@ fn complete() { -h Print help action action alias alias +complete Register shell completions for this program help Print this message or the help of the given subcommand(s) hint hint last last diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs index 6575c92a..ed87fc29 100644 --- a/clap_complete/tests/testsuite/fish.rs +++ b/clap_complete/tests/testsuite/fish.rs @@ -138,8 +138,8 @@ fn complete() { let input = "test \t"; let expected = r#"% test -action help (Print this message or the help of the given subcommand(s)) last quote -alias hint pacman value"#; +action complete (Register shell completions for this program) hint pacman value +alias help (Print this message or the help of the given subcommand(s)) last quote"#; let actual = runtime.complete(input, &term).unwrap(); snapbox::assert_eq(expected, actual); } diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs index e33b22a5..c9760aba 100644 --- a/clap_complete/tests/testsuite/zsh.rs +++ b/clap_complete/tests/testsuite/zsh.rs @@ -138,8 +138,9 @@ fn complete() { let input = "test \t"; let expected = r#"% test -help -- Print this message or the help of the given subcommand(s) -pacman action alias value quote hint last --"#; +complete -- Register shell completions for this program +help -- Print this message or the help of the given subcommand(s) +pacman action alias value quote hint last --"#; let actual = runtime.complete(input, &term).unwrap(); snapbox::assert_eq(expected, actual); }