Repository: clap-rs/clap
Pull request #5662 (2024-08-11 00:39:59)

fix(complete)!: Adjust built-in completion CLI
<!--
Thanks for helping out!

Please link the appropriate issue from your PR.

If you don't have an issue, we'd recommend starting with one first so the PR can focus on the
implementation (unless its an obvious bug or documentation fix that will have
little conversation).
-->



Patch:
diff --git a/clap_complete/src/dynamic/shells/command.rs b/clap_complete/src/dynamic/shells/command.rs
index eae385cc..36db2fb9 100644
--- a/clap_complete/src/dynamic/shells/command.rs
+++ b/clap_complete/src/dynamic/shells/command.rs
@@ -50,27 +50,27 @@ use super::Shell;
 ///
 /// Bash
 /// ```bash
-/// echo "source <(your_program complete --shell bash --register -)" >> ~/.bashrc
+/// echo "source <(your_program complete bash --register -)" >> ~/.bashrc
 /// ```
 ///
 /// Elvish
 /// ```elvish
-/// echo "eval (your_program complete --shell elvish --register -)" >> ~/.elvish/rc.elv
+/// echo "eval (your_program complete elvish --register -)" >> ~/.elvish/rc.elv
 /// ```
 ///
 /// Fish
 /// ```fish
-/// echo "source (your_program complete --shell fish --register - | psub)" >> ~/.config/fish/config.fish
+/// echo "source (your_program complete fish --register - | psub)" >> ~/.config/fish/config.fish
 /// ```
 ///
 /// Powershell
 /// ```powershell
-/// echo "your_program complete --shell powershell --register - | Invoke-Expression" >> $PROFILE
+/// echo "your_program complete powershell --register - | Invoke-Expression" >> $PROFILE
 /// ```
 ///
 /// Zsh
 /// ```zsh
-/// echo "source <(your_program complete --shell zsh --register -)" >> ~/.zshrc
+/// echo "source <(your_program complete zsh --register -)" >> ~/.zshrc
 /// ```
 #[derive(clap::Subcommand)]
 #[allow(missing_docs)]
@@ -149,46 +149,37 @@ impl CompleteCommand {
 ///
 /// Bash
 /// ```bash
-/// echo "source <(your_program complete --shell bash)" >> ~/.bashrc
+/// echo "source <(your_program complete bash)" >> ~/.bashrc
 /// ```
 ///
 /// Elvish
 /// ```elvish
-/// echo "eval (your_program complete --shell elvish)" >> ~/.elvish/rc.elv
+/// echo "eval (your_program complete elvish)" >> ~/.elvish/rc.elv
 /// ```
 ///
 /// Fish
 /// ```fish
-/// echo "source (your_program complete --shell fish | psub)" >> ~/.config/fish/config.fish
+/// echo "source (your_program complete fish | psub)" >> ~/.config/fish/config.fish
 /// ```
 ///
 /// Powershell
 /// ```powershell
-/// echo "your_program complete --shell powershell | Invoke-Expression" >> $PROFILE
+/// echo "your_program complete powershell | Invoke-Expression" >> $PROFILE
 /// ```
 ///
 /// Zsh
 /// ```zsh
-/// echo "source <(your_program complete --shell zsh)" >> ~/.zshrc
+/// echo "source <(your_program complete zsh)" >> ~/.zshrc
 /// ```
 #[derive(clap::Args, Clone, Debug)]
 #[command(about = None, long_about = None)]
 pub struct CompleteArgs {
-    /// Path to write completion-registration to
-    #[arg(long, value_name = "PATH")]
-    register: Option<std::path::PathBuf>,
-
-    #[arg(
-        raw = true,
-        value_name = "ARG",
-        hide = true,
-        conflicts_with = "register"
-    )]
-    comp_words: Option<Vec<OsString>>,
-
     /// Specify shell to complete for
-    #[arg(long, value_name = "NAME")]
+    #[arg(value_name = "NAME")]
     shell: Option<Shell>,
+
+    #[arg(raw = true, value_name = "ARG", hide = true)]
+    comp_words: Option<Vec<OsString>>,
 }
 
 impl CompleteArgs {
@@ -219,23 +210,12 @@ impl CompleteArgs {
             shell.write_complete(cmd, comp_words.clone(), current_dir.as_deref(), &mut buf)?;
             std::io::stdout().write_all(&buf)?;
         } else {
-            let out_path = self
-                .register
-                .as_deref()
-                .unwrap_or(std::path::Path::new("-"));
             let name = cmd.get_name();
             let bin = cmd.get_bin_name().unwrap_or_else(|| cmd.get_name());
 
             let mut buf = Vec::new();
             shell.write_registration(name, bin, bin, &mut buf)?;
-            if out_path == std::path::Path::new("-") {
-                std::io::stdout().write_all(&buf)?;
-            } else if out_path.is_dir() {
-                let out_path = out_path.join(shell.file_name(name));
-                std::fs::write(out_path, buf)?;
-            } else {
-                std::fs::write(out_path, buf)?;
-            }
+            std::io::stdout().write_all(&buf)?;
         }
 
         Ok(())
@@ -310,7 +290,7 @@ _clap_complete_NAME() {
     else
         export _CLAP_COMPLETE_SPACE=true
     fi
-    COMPREPLY=( $("COMPLETER" complete --shell bash -- "${COMP_WORDS[@]}") )
+    COMPREPLY=( $("COMPLETER" complete bash -- "${COMP_WORDS[@]}") )
     if [[ $? != 0 ]]; then
         unset COMPREPLY
     elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
@@ -421,7 +401,7 @@ set edit:completion:arg-completer[BIN] = { |@words|
     set index = (- $index 1)
     set E:_CLAP_COMPLETE_INDEX = (to-string $index)
 
-    put (COMPLETER complete --shell elvish -- $@words) | to-lines
+    put (COMPLETER complete elvish -- $@words) | to-lines
 }
 "#
         .replace("COMPLETER", &completer)
@@ -471,7 +451,7 @@ impl ShellCompleter for super::Fish {
 
         writeln!(
             buf,
-            r#"complete -x -c {bin} -a "("'{completer}'" complete --shell fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""#
+            r#"complete -x -c {bin} -a "("'{completer}'" complete fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))""#
         )
     }
     fn write_complete(
@@ -521,7 +501,7 @@ impl ShellCompleter for super::Powershell {
 Register-ArgumentCompleter -Native -CommandName {bin} -ScriptBlock {{
     param($wordToComplete, $commandAst, $cursorPosition)
 
-    $results = Invoke-Expression "&{completer} complete --shell powershell -- $($commandAst.ToString())";
+    $results = Invoke-Expression "&{completer} complete powershell -- $($commandAst.ToString())";
     $results | ForEach-Object {{
         $split = $_.Split("`t");
         $cmd = $split[0];
@@ -585,7 +565,7 @@ function _clap_dynamic_completer() {
     export _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
     export _CLAP_IFS=$'\n'
 
-    local completions=("${(@f)$(COMPLETER complete --shell zsh -- ${words} 2>/dev/null)}")
+    local completions=("${(@f)$(COMPLETER complete zsh -- ${words} 2>/dev/null)}")
 
     if [[ -n $completions ]]; then
         compadd -a completions


Test patch:
diff --git a/clap_complete/tests/snapshots/home/dynamic/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/dynamic/exhaustive/bash/.bashrc
index f1bd2f1f..b10dc26e 100644
--- a/clap_complete/tests/snapshots/home/dynamic/exhaustive/bash/.bashrc
+++ b/clap_complete/tests/snapshots/home/dynamic/exhaustive/bash/.bashrc
@@ -10,7 +10,7 @@ _clap_complete_exhaustive() {
     else
         export _CLAP_COMPLETE_SPACE=true
     fi
-    COMPREPLY=( $("exhaustive" complete --shell bash -- "${COMP_WORDS[@]}") )
+    COMPREPLY=( $("exhaustive" complete bash -- "${COMP_WORDS[@]}") )
     if [[ $? != 0 ]]; then
         unset COMPREPLY
     elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
diff --git a/clap_complete/tests/snapshots/home/dynamic/exhaustive/elvish/elvish/rc.elv b/clap_complete/tests/snapshots/home/dynamic/exhaustive/elvish/elvish/rc.elv
index 2e54dd4f..614e4a29 100644
--- a/clap_complete/tests/snapshots/home/dynamic/exhaustive/elvish/elvish/rc.elv
+++ b/clap_complete/tests/snapshots/home/dynamic/exhaustive/elvish/elvish/rc.elv
@@ -8,7 +8,7 @@ set edit:completion:arg-completer[exhaustive] = { |@words|
     set index = (- $index 1)
     set E:_CLAP_COMPLETE_INDEX = (to-string $index)
 
-    put (exhaustive complete --shell elvish -- $@words) | to-lines
+    put (exhaustive complete elvish -- $@words) | to-lines
 }
 
 
diff --git a/clap_complete/tests/snapshots/home/dynamic/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/dynamic/exhaustive/fish/fish/completions/exhaustive.fish
index cfb35d74..34301ce9 100644
--- a/clap_complete/tests/snapshots/home/dynamic/exhaustive/fish/fish/completions/exhaustive.fish
+++ b/clap_complete/tests/snapshots/home/dynamic/exhaustive/fish/fish/completions/exhaustive.fish
@@ -1,1 +1,1 @@
-complete -x -c exhaustive -a "("'exhaustive'" complete --shell fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))"
+complete -x -c exhaustive -a "("'exhaustive'" complete fish -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))"
diff --git a/clap_complete/tests/snapshots/home/dynamic/exhaustive/zsh/zsh/_exhaustive b/clap_complete/tests/snapshots/home/dynamic/exhaustive/zsh/zsh/_exhaustive
index 97d2d1ab..a7082462 100644
--- a/clap_complete/tests/snapshots/home/dynamic/exhaustive/zsh/zsh/_exhaustive
+++ b/clap_complete/tests/snapshots/home/dynamic/exhaustive/zsh/zsh/_exhaustive
@@ -3,7 +3,7 @@ function _clap_dynamic_completer() {
     export _CLAP_COMPLETE_INDEX=$(expr $CURRENT - 1)
     export _CLAP_IFS=$'\n'
 
-    local completions=("${(@f)$(exhaustive complete --shell zsh -- ${words} 2>/dev/null)}")
+    local completions=("${(@f)$(exhaustive complete zsh -- ${words} 2>/dev/null)}")
 
     if [[ -n $completions ]]; then
         compadd -a completions
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc
index f99506a4..68a5a1a1 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc
@@ -238,20 +238,12 @@ _exhaustive() {
             return 0
             ;;
         exhaustive__complete)
-            opts="-h -V --register --shell --global --help --version [ARG]..."
+            opts="-h -V --global --help --version bash elvish fish powershell zsh [ARG]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
             fi
             case "${prev}" in
-                --register)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --shell)
-                    COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}"))
-                    return 0
-                    ;;
                 *)
                     COMPREPLY=()
                     ;;
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv b/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv
index 1b63da9f..2fc89206 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/elvish/elvish/rc.elv
@@ -239,8 +239,6 @@ set edit:completion:arg-completer[exhaustive] = {|@words|
             cand --version 'Print version'
         }
         &'exhaustive;complete'= {
-            cand --register 'Path to write completion-registration to'
-            cand --shell 'Specify shell to complete for'
             cand --global 'everywhere'
             cand -h 'Print help'
             cand --help 'Print help'
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
index b66c67ef..92d6d42e 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/fish/fish/completions/exhaustive.fish
@@ -136,8 +136,6 @@ complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l email -r
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -l global -d 'everywhere'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s h -l help -d 'Print help'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand hint" -s V -l version -d 'Print version'
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l register -d 'Path to write completion-registration to' -r -F
-complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l shell -d 'Specify shell to complete for' -r -f -a "{bash\t'',elvish\t'',fish\t'',powershell\t'',zsh\t''}"
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -l global -d 'everywhere'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -s h -l help -d 'Print help'
 complete -c exhaustive -n "__fish_exhaustive_using_subcommand complete" -s V -l version -d 'Print version'
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive
index 88571e04..73f57fdf 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/zsh/_exhaustive
@@ -325,13 +325,12 @@ _arguments "${_arguments_options[@]}" : \
 ;;
 (complete)
 _arguments "${_arguments_options[@]}" : \
-'--register=[Path to write completion-registration to]:PATH:_files' \
-'--shell=[Specify shell to complete for]:NAME:(bash elvish fish powershell zsh)' \
 '--global[everywhere]' \
 '-h[Print help]' \
 '--help[Print help]' \
 '-V[Print version]' \
 '--version[Print version]' \
+'::shell -- Specify shell to complete for:(bash elvish fish powershell zsh)' \
 '*::comp_words:' \
 && ret=0
 ;;
diff --git a/clap_complete/tests/snapshots/register_minimal.bash b/clap_complete/tests/snapshots/register_minimal.bash
index 7198bfe1..a111d3d9 100644
--- a/clap_complete/tests/snapshots/register_minimal.bash
+++ b/clap_complete/tests/snapshots/register_minimal.bash
@@ -8,7 +8,7 @@ _clap_complete_my_app() {
     else
         export _CLAP_COMPLETE_SPACE=true
     fi
-    COMPREPLY=( $("my-app" complete --shell bash -- "${COMP_WORDS[@]}") )
+    COMPREPLY=( $("my-app" complete bash -- "${COMP_WORDS[@]}") )
     if [[ $? != 0 ]]; then
         unset COMPREPLY
     elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
diff --git a/clap_complete/tests/testsuite/common.rs b/clap_complete/tests/testsuite/common.rs
index 3534344b..29d0f6df 100644
--- a/clap_complete/tests/testsuite/common.rs
+++ b/clap_complete/tests/testsuite/common.rs
@@ -330,11 +330,7 @@ pub(crate) fn register_example<R: completest::RuntimeBuilder>(context: &str, nam
     let mut registration = std::process::Command::new(&bin_path);
     match context {
         "static" => registration.args([format!("--generate={shell_name}")]),
-        "dynamic" => registration.args([
-            "complete".to_owned(),
-            "--register=-".to_owned(),
-            format!("--shell={shell_name}"),
-        ]),
+        "dynamic" => registration.args(["complete", shell_name]),
         _ => unreachable!("unsupported context {}", context),
     };
     let registration = registration.output().unwrap();