Repository: clap-rs/clap
Pull request #5584 (2024-07-17 09:10:19)

feat(clap_complete): Support hiding subcommands and their aliases
<!--
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).
-->
Based on PR: https://github.com/clap-rs/clap/pull/5583
Related issue: https://github.com/clap-rs/clap/issues/3951

#### Work in this PR
Support hiding subcommands and their aliases. The specific behavior is reflected in the differences between the test commit and the feat commit.



Patch:
diff --git a/clap_builder/src/builder/command.rs b/clap_builder/src/builder/command.rs
index 78f3e75a..f67eb9c6 100644
--- a/clap_builder/src/builder/command.rs
+++ b/clap_builder/src/builder/command.rs
@@ -3553,6 +3553,15 @@ impl Command {
         self.long_flag_aliases.iter().map(|a| a.0.as_str())
     }
 
+    /// Iterate through the *hidden* aliases for this subcommand.
+    #[inline]
+    pub fn get_aliases(&self) -> impl Iterator<Item = &str> + '_ {
+        self.aliases
+            .iter()
+            .filter(|(_, vis)| !*vis)
+            .map(|a| a.0.as_str())
+    }
+
     #[inline]
     pub(crate) fn is_set(&self, s: AppSettings) -> bool {
         self.settings.is_set(s) || self.g_settings.is_set(s)
diff --git a/clap_complete/src/dynamic/completer.rs b/clap_complete/src/dynamic/completer.rs
index 640e2d57..6a650f74 100644
--- a/clap_complete/src/dynamic/completer.rs
+++ b/clap_complete/src/dynamic/completer.rs
@@ -386,11 +386,18 @@ fn subcommands(p: &clap::Command) -> Vec<CompletionCandidate> {
     debug!("subcommands: Has subcommands...{:?}", p.has_subcommands());
     p.get_subcommands()
         .flat_map(|sc| {
-            sc.get_name_and_visible_aliases().into_iter().map(|s| {
-                CompletionCandidate::new(s.to_string())
-                    .help(sc.get_about().cloned())
-                    .visible(true)
-            })
+            sc.get_name_and_visible_aliases()
+                .into_iter()
+                .map(|s| {
+                    CompletionCandidate::new(s.to_string())
+                        .help(sc.get_about().cloned())
+                        .visible(!sc.is_hide_set())
+                })
+                .chain(sc.get_aliases().into_iter().map(|s| {
+                    CompletionCandidate::new(s.to_string())
+                        .help(sc.get_about().cloned())
+                        .visible(false)
+                }))
         })
         .collect()
 }


Test patch:
diff --git a/clap_complete/tests/testsuite/dynamic.rs b/clap_complete/tests/testsuite/dynamic.rs
index df8bac01..2863da32 100644
--- a/clap_complete/tests/testsuite/dynamic.rs
+++ b/clap_complete/tests/testsuite/dynamic.rs
@@ -54,6 +54,44 @@ fn suggest_hidden_long_flags() {
     )
 }
 
+#[test]
+fn suggest_hidden_subcommand_and_aliases() {
+    let mut cmd = Command::new("exhaustive")
+        .subcommand(
+            Command::new("test_visible")
+                .visible_alias("test_visible-alias_visible")
+                .alias("test_visible-alias_hidden"),
+        )
+        .subcommand(
+            Command::new("test_hidden")
+                .visible_alias("test_hidden-alias_visible")
+                .alias("test_hidden-alias_hidden")
+                .hide(true),
+        );
+
+    assert_data_eq!(
+        complete!(cmd, "test"),
+        snapbox::str![
+            "test_visible
+test_visible-alias_visible"
+        ]
+    );
+
+    assert_data_eq!(
+        complete!(cmd, "test_h"),
+        snapbox::str![
+            "test_hidden
+test_hidden-alias_hidden
+test_hidden-alias_visible"
+        ]
+    );
+
+    assert_data_eq!(
+        complete!(cmd, "test_hidden-alias_h"),
+        snapbox::str!["test_hidden-alias_hidden"]
+    )
+}
+
 #[test]
 fn suggest_subcommand_aliases() {
     let mut cmd = Command::new("exhaustive")
diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs
index 2aa95f7e..41a5e109 100644
--- a/clap_complete/tests/testsuite/elvish.rs
+++ b/clap_complete/tests/testsuite/elvish.rs
@@ -196,8 +196,8 @@ fn complete_dynamic() {
     let expected = snapbox::str![
         r#"% exhaustive --generate
  COMPLETING argument  
---generate  --help     -V  action  complete  hint  pacman  value
---global    --version  -h  alias   help      last  quote "#
+--generate  --help     -V  action  help  last    quote
+--global    --version  -h  alias   hint  pacman  value"#
     ];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
@@ -213,4 +213,4 @@ fn complete_dynamic() {
     ];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
-}
\ No newline at end of file
+}
diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs
index c6c11a4e..b35458fd 100644
--- a/clap_complete/tests/testsuite/fish.rs
+++ b/clap_complete/tests/testsuite/fish.rs
@@ -185,11 +185,11 @@ fn complete_dynamic() {
     let input = "exhaustive \t\t";
     let expected = snapbox::str![[r#"
 % exhaustive action 
-action                                                             last              -V         (Print version)
-alias                                                              pacman            --generate      (generate)
-complete            (Register shell completions for this program)  quote             --global      (everywhere)
-help  (Print this message or the help of the given subcommand(s))  value             --help        (Print help)
-hint                                                               -h  (Print help)  --version  (Print version)
+action                                                             pacman               --generate      (generate)
+alias                                                              quote                --global      (everywhere)
+help  (Print this message or the help of the given subcommand(s))  value                --help        (Print help)
+hint                                                               -h     (Print help)  --version  (Print version)
+last                                                               -V  (Print version)  
 "#]];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs
index e483e7fd..ea8a1436 100644
--- a/clap_complete/tests/testsuite/zsh.rs
+++ b/clap_complete/tests/testsuite/zsh.rs
@@ -163,7 +163,6 @@ pacman    action  alias  value  quote  hint  last  --
     assert_data_eq!(actual, expected);
 }
 
-
 #[cfg(all(unix, feature = "unstable-dynamic"))]
 #[test]
 fn register_dynamic() {
@@ -184,8 +183,8 @@ fn complete_dynamic() {
     let input = "exhaustive \t\t";
     let expected = snapbox::str![
         r#"% exhaustive
---generate  --help      -V          action      complete    hint        pacman      value       
---global    --version   -h          alias       help        last        quote       "#
+--generate  --help      -V          action      help        last        quote       
+--global    --version   -h          alias       hint        pacman      value       "#
     ];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
@@ -200,4 +199,4 @@ fn complete_dynamic() {
     ];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
-}
\ No newline at end of file
+}