Repository: clap-rs/clap
Pull request #5585 (2024-07-17 09:12:15)

feat(clap_complete): Support hiding possible values
<!--
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/5584
Related to https://github.com/clap-rs/clap/issues/3951

#### Work in this PR
Support hiding possible values. The specific behavior is reflected in the differences between the test commit and the feat commit.


epage: This has a merge conflict

Patch:
diff --git a/clap_complete/src/dynamic/completer.rs b/clap_complete/src/dynamic/completer.rs
index 6a650f74..19966f47 100644
--- a/clap_complete/src/dynamic/completer.rs
+++ b/clap_complete/src/dynamic/completer.rs
@@ -110,15 +110,14 @@ fn complete_arg(
                         completions.extend(
                             complete_arg_value(value.to_str().ok_or(value), arg, current_dir)
                                 .into_iter()
-                                .map(|(os, help)| {
-                                    // HACK: Need better `OsStr` manipulation
+                                .map(|comp| {
                                     CompletionCandidate::new(format!(
                                         "--{}={}",
                                         flag,
-                                        os.to_string_lossy()
+                                        comp.get_content().to_string_lossy()
                                     ))
-                                    .help(help)
-                                    .visible(true)
+                                    .help(comp.get_help().cloned())
+                                    .visible(comp.is_visible())
                                 }),
                         );
                     }
@@ -169,11 +168,7 @@ fn complete_arg(
         .get_positionals()
         .find(|p| p.get_index() == Some(pos_index))
     {
-        completions.extend(
-            complete_arg_value(arg.to_value(), positional, current_dir)
-                .into_iter()
-                .map(|(os, help)| CompletionCandidate::new(os).help(help).visible(true)),
-        );
+        completions.extend(complete_arg_value(arg.to_value(), positional, current_dir).into_iter());
     }
 
     if let Ok(value) = arg.to_value() {
@@ -191,7 +186,7 @@ fn complete_arg_value(
     value: Result<&str, &OsStr>,
     arg: &clap::Arg,
     current_dir: Option<&std::path::Path>,
-) -> Vec<(OsString, Option<StyledStr>)> {
+) -> Vec<CompletionCandidate> {
     let mut values = Vec::new();
     debug!("complete_arg_value: arg={arg:?}, value={value:?}");
 
@@ -199,8 +194,11 @@ fn complete_arg_value(
         if let Ok(value) = value {
             values.extend(possible_values.into_iter().filter_map(|p| {
                 let name = p.get_name();
-                name.starts_with(value)
-                    .then(|| (OsString::from(name), p.get_help().cloned()))
+                name.starts_with(value).then(|| {
+                    CompletionCandidate::new(OsString::from(name))
+                        .help(p.get_help().cloned())
+                        .visible(!p.is_hide_set())
+                })
             }));
         }
     } else {
@@ -249,7 +247,7 @@ fn complete_path(
     value_os: &OsStr,
     current_dir: Option<&std::path::Path>,
     is_wanted: impl Fn(&std::path::Path) -> bool,
-) -> Vec<(OsString, Option<StyledStr>)> {
+) -> Vec<CompletionCandidate> {
     let mut completions = Vec::new();
 
     let current_dir = match current_dir {
@@ -281,12 +279,20 @@ fn complete_path(
             let path = entry.path();
             let mut suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
             suggestion.push(""); // Ensure trailing `/`
-            completions.push((suggestion.as_os_str().to_owned(), None));
+            completions.push(
+                CompletionCandidate::new(suggestion.as_os_str().to_owned())
+                    .help(None)
+                    .visible(true),
+            );
         } else {
             let path = entry.path();
             if is_wanted(&path) {
                 let suggestion = pathdiff::diff_paths(&path, current_dir).unwrap_or(path);
-                completions.push((suggestion.as_os_str().to_owned(), None));
+                completions.push(
+                    CompletionCandidate::new(suggestion.as_os_str().to_owned())
+                        .help(None)
+                        .visible(true),
+                );
             }
         }
     }


Test patch:
diff --git a/clap_complete/tests/testsuite/dynamic.rs b/clap_complete/tests/testsuite/dynamic.rs
index 2863da32..6eff7f29 100644
--- a/clap_complete/tests/testsuite/dynamic.rs
+++ b/clap_complete/tests/testsuite/dynamic.rs
@@ -122,6 +122,28 @@ hello-world-foo"
     );
 }
 
+#[test]
+fn suggest_hidden_possible_value() {
+    let mut cmd = Command::new("exhaustive").arg(
+        clap::Arg::new("possible_value").long("test").value_parser([
+            PossibleValue::new("test-visible").help("Say hello to the world"),
+            PossibleValue::new("test-hidden")
+                .help("Say hello to the moon")
+                .hide(true),
+        ]),
+    );
+
+    assert_data_eq!(
+        complete!(cmd, "--test=test"),
+        snapbox::str!["--test=test-visible\tSay hello to the world"]
+    );
+
+    assert_data_eq!(
+        complete!(cmd, "--test=test-h"),
+        snapbox::str!["--test=test-hidden\tSay hello to the moon"]
+    )
+}
+
 #[test]
 fn suggest_hidden_long_flag_aliases() {
     let mut cmd = Command::new("exhaustive")