Repository: clap-rs/clap
Pull request #5741 (2024-09-20 18:44:25)

fix(complete): When completing -[TAB], prioritize shorts
This is part of #5058


Patch:
diff --git a/clap_complete/src/engine/complete.rs b/clap_complete/src/engine/complete.rs
index fb0463e4..9ed091b8 100644
--- a/clap_complete/src/engine/complete.rs
+++ b/clap_complete/src/engine/complete.rs
@@ -157,7 +157,40 @@ fn complete_arg(
                 completions.extend(complete_arg_value(arg.to_value(), positional, current_dir));
             }
 
-            if let Some((flag, value)) = arg.to_long() {
+            if arg.is_empty() {
+                completions.extend(longs_and_visible_aliases(cmd));
+                completions.extend(hidden_longs_aliases(cmd));
+
+                let dash_or_arg = if arg.is_empty() {
+                    "-".into()
+                } else {
+                    arg.to_value_os().to_string_lossy()
+                };
+                completions.extend(
+                    shorts_and_visible_aliases(cmd)
+                        .into_iter()
+                        .map(|comp| comp.add_prefix(dash_or_arg.to_string())),
+                );
+            } else if arg.is_stdio() {
+                // HACK: Assuming knowledge of is_stdio
+                let dash_or_arg = if arg.is_empty() {
+                    "-".into()
+                } else {
+                    arg.to_value_os().to_string_lossy()
+                };
+                completions.extend(
+                    shorts_and_visible_aliases(cmd)
+                        .into_iter()
+                        .map(|comp| comp.add_prefix(dash_or_arg.to_string())),
+                );
+
+                completions.extend(longs_and_visible_aliases(cmd));
+                completions.extend(hidden_longs_aliases(cmd));
+            } else if arg.is_escape() {
+                // HACK: Assuming knowledge of is_escape
+                completions.extend(longs_and_visible_aliases(cmd));
+                completions.extend(hidden_longs_aliases(cmd));
+            } else if let Some((flag, value)) = arg.to_long() {
                 if let Ok(flag) = flag {
                     if let Some(value) = value {
                         if let Some(arg) = cmd.get_arguments().find(|a| a.get_long() == Some(flag))
@@ -172,31 +205,11 @@ fn complete_arg(
                         completions.extend(longs_and_visible_aliases(cmd).into_iter().filter(
                             |comp| comp.get_value().starts_with(format!("--{}", flag).as_str()),
                         ));
-
                         completions.extend(hidden_longs_aliases(cmd).into_iter().filter(|comp| {
                             comp.get_value().starts_with(format!("--{}", flag).as_str())
                         }));
                     }
                 }
-            } else if arg.is_escape() || arg.is_stdio() || arg.is_empty() {
-                // HACK: Assuming knowledge of is_escape / is_stdio
-                completions.extend(longs_and_visible_aliases(cmd));
-
-                completions.extend(hidden_longs_aliases(cmd));
-            }
-
-            if arg.is_empty() || arg.is_stdio() {
-                let dash_or_arg = if arg.is_empty() {
-                    "-".into()
-                } else {
-                    arg.to_value_os().to_string_lossy()
-                };
-                // HACK: Assuming knowledge of is_stdio
-                completions.extend(
-                    shorts_and_visible_aliases(cmd)
-                        .into_iter()
-                        .map(|comp| comp.add_prefix(dash_or_arg.to_string())),
-                );
             } else if let Some(short) = arg.to_short() {
                 if !short.is_negative_number() {
                     // Find the first takes_values option.
@@ -434,7 +447,11 @@ fn shorts_and_visible_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
             a.get_short_and_visible_aliases().map(|shorts| {
                 shorts.into_iter().map(|s| {
                     CompletionCandidate::new(s.to_string())
-                        .help(a.get_help().cloned())
+                        .help(
+                            a.get_help()
+                                .cloned()
+                                .or_else(|| a.get_long().map(|long| format!("--{long}").into())),
+                        )
                         .id(Some(format!("arg::{}", a.get_id())))
                         .hide(a.is_hide_set())
                 })


Test patch:
diff --git a/clap_complete/tests/testsuite/engine.rs b/clap_complete/tests/testsuite/engine.rs
index 8659cc38..59a5a64d 100644
--- a/clap_complete/tests/testsuite/engine.rs
+++ b/clap_complete/tests/testsuite/engine.rs
@@ -73,15 +73,9 @@ fn suggest_hidden_subcommand_and_aliases() {
                 .hide(true),
         );
 
-    assert_data_eq!(
-        complete!(cmd, "test"),
-        snapbox::str!["test_visible"]
-    );
+    assert_data_eq!(complete!(cmd, "test"), snapbox::str!["test_visible"]);
 
-    assert_data_eq!(
-        complete!(cmd, "test_h"),
-        snapbox::str!["test_hidden"]
-    );
+    assert_data_eq!(complete!(cmd, "test_h"), snapbox::str!["test_hidden"]);
 
     assert_data_eq!(
         complete!(cmd, "test_hidden-alias_h"),
@@ -156,15 +150,9 @@ fn suggest_hidden_long_flag_aliases() {
                 .hide(true),
         );
 
-    assert_data_eq!(
-        complete!(cmd, "--test"),
-        snapbox::str!["--test_visible"]
-    );
+    assert_data_eq!(complete!(cmd, "--test"), snapbox::str!["--test_visible"]);
 
-    assert_data_eq!(
-        complete!(cmd, "--test_h"),
-        snapbox::str!["--test_hidden"]
-    );
+    assert_data_eq!(complete!(cmd, "--test_h"), snapbox::str!["--test_hidden"]);
 
     assert_data_eq!(
         complete!(cmd, "--test_visible-alias_h"),
@@ -801,17 +789,23 @@ fn suggest_delimiter_values() {
                 .value_delimiter(','),
         );
 
-    assert_data_eq!(complete!(cmd, "--delimiter [TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "--delimiter [TAB]"),
+        snapbox::str![[r#"
 comma
 space
 tab
-"#]]);
+"#]]
+    );
 
-    assert_data_eq!(complete!(cmd, "--delimiter=[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "--delimiter=[TAB]"),
+        snapbox::str![[r#"
 --delimiter=comma
 --delimiter=space
 --delimiter=tab
-"#]]);
+"#]]
+    );
 
     assert_data_eq!(complete!(cmd, "--delimiter c[TAB]"), snapbox::str!["comma"]);
 
@@ -820,20 +814,26 @@ tab
         snapbox::str!["--delimiter=comma"]
     );
 
-    assert_data_eq!(complete!(cmd, "--delimiter comma,[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "--delimiter comma,[TAB]"),
+        snapbox::str![[r#"
 comma,comma
 comma,space
 comma,tab
-"#]]);
+"#]]
+    );
 
-    assert_data_eq!(complete!(cmd, "--delimiter=comma,[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "--delimiter=comma,[TAB]"),
+        snapbox::str![[r#"
 --delimiter=comma,a_pos
 --delimiter=comma,b_pos
 --delimiter=comma,c_pos
 --delimiter=comma,comma
 --delimiter=comma,space
 --delimiter=comma,tab
-"#]]);
+"#]]
+    );
 
     assert_data_eq!(
         complete!(cmd, "--delimiter comma,s[TAB]"),
@@ -845,36 +845,48 @@ comma,tab
         snapbox::str!["--delimiter=comma,space"]
     );
 
-    assert_data_eq!(complete!(cmd, "-D [TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "-D [TAB]"),
+        snapbox::str![[r#"
 comma
 space
 tab
-"#]]);
+"#]]
+    );
 
-    assert_data_eq!(complete!(cmd, "-D=[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "-D=[TAB]"),
+        snapbox::str![[r#"
 -D=comma
 -D=space
 -D=tab
-"#]]);
+"#]]
+    );
 
     assert_data_eq!(complete!(cmd, "-D c[TAB]"), snapbox::str!["comma"]);
 
     assert_data_eq!(complete!(cmd, "-D=c[TAB]"), snapbox::str!["-D=comma"]);
 
-    assert_data_eq!(complete!(cmd, "-D comma,[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "-D comma,[TAB]"),
+        snapbox::str![[r#"
 comma,comma
 comma,space
 comma,tab
-"#]]);
+"#]]
+    );
 
-    assert_data_eq!(complete!(cmd, "-D=comma,[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "-D=comma,[TAB]"),
+        snapbox::str![[r#"
 -D=comma,a_pos
 -D=comma,b_pos
 -D=comma,c_pos
 -D=comma,comma
 -D=comma,space
 -D=comma,tab
-"#]]);
+"#]]
+    );
 
     assert_data_eq!(
         complete!(cmd, "-D comma,s[TAB]"),
@@ -886,19 +898,25 @@ comma,tab
         snapbox::str!["-D=comma,space"]
     );
 
-    assert_data_eq!(complete!(cmd, "-- [TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "-- [TAB]"),
+        snapbox::str![[r#"
 a_pos
 b_pos
 c_pos
 --delimiter
 --help	Print help
-"#]]);
+"#]]
+    );
 
-    assert_data_eq!(complete!(cmd, " -- a_pos,[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, " -- a_pos,[TAB]"),
+        snapbox::str![[r#"
 a_pos,a_pos
 a_pos,b_pos
 a_pos,c_pos
-"#]]);
+"#]]
+    );
 
     assert_data_eq!(
         complete!(cmd, "-- a_pos,b[TAB]"),
@@ -923,17 +941,23 @@ fn suggest_allow_hyphen() {
     assert_data_eq!(complete!(cmd, "--format --t[TAB]"), snapbox::str!["--toml"]);
     assert_data_eq!(complete!(cmd, "-F --t[TAB]"), snapbox::str!["--toml"]);
 
-    assert_data_eq!(complete!(cmd, "--format --[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "--format --[TAB]"),
+        snapbox::str![[r#"
 --json
 --toml
 --yaml
-"#]]);
+"#]]
+    );
 
-    assert_data_eq!(complete!(cmd, "-F --[TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "-F --[TAB]"),
+        snapbox::str![[r#"
 --json
 --toml
 --yaml
-"#]]);
+"#]]
+    );
 
     assert_data_eq!(
         complete!(cmd, "--format --json --j[TAB]"),
@@ -985,11 +1009,14 @@ pos_b
 --help	Print help
 "#]]
     );
-    assert_data_eq!(complete!(cmd, "-F --json --pos_a [TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "-F --json --pos_a [TAB]"),
+        snapbox::str![[r#"
 pos_b
 --format
 --help	Print help
-"#]]);
+"#]]
+    );
 
     assert_data_eq!(
         complete!(cmd, "--format --json --pos_a p[TAB]"),
@@ -1023,16 +1050,22 @@ fn suggest_positional_short_allow_hyphen() {
                 .value_parser(["pos_b"]),
         );
 
-    assert_data_eq!(complete!(cmd, "--format --json -a [TAB]"), snapbox::str![[r#"
+    assert_data_eq!(
+        complete!(cmd, "--format --json -a [TAB]"),
+        snapbox::str![[r#"
 pos_b
 --format
 --help	Print help
-"#]]);
-    assert_data_eq!(complete!(cmd, "-F --json -a [TAB]"), snapbox::str![[r#"
+"#]]
+    );
+    assert_data_eq!(
+        complete!(cmd, "-F --json -a [TAB]"),
+        snapbox::str![[r#"
 pos_b
 --format
 --help	Print help
-"#]]);
+"#]]
+    );
 
     assert_data_eq!(
         complete!(cmd, "--format --json -a p[TAB]"),
@@ -1081,11 +1114,20 @@ pos-c
     assert_data_eq!(
         complete!(cmd, "-[TAB]"),
         snapbox::str![[r#"
+-r	--required-flag
+-o	--optional-flag
+-s
+-h	Print help
+--long-flag
+"#]]
+    );
+    assert_data_eq!(
+        complete!(cmd, "--[TAB]"),
+        snapbox::str![[r#"
 --required-flag
 --optional-flag
 --long-flag
 --help	Print help
--s
 "#]]
     );
 }