Merge pull request #5740 from epage/complete-id

fix(complete): De-duplicate built-in candidates
This commit is contained in:
Ed Page 2024-09-20 14:27:41 -04:00 committed by GitHub
commit 3af629a141
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 68 additions and 100 deletions

View file

@ -8,6 +8,7 @@ use clap::builder::StyledStr;
pub struct CompletionCandidate { pub struct CompletionCandidate {
value: OsString, value: OsString,
help: Option<StyledStr>, help: Option<StyledStr>,
id: Option<String>,
hidden: bool, hidden: bool,
} }
@ -27,6 +28,14 @@ impl CompletionCandidate {
self self
} }
/// Only first for a given Id is shown
///
/// To reduce the risk of conflicts, this should likely contain a namespace.
pub fn id(mut self, id: Option<String>) -> Self {
self.id = id;
self
}
/// Set the visibility of the completion candidate /// Set the visibility of the completion candidate
/// ///
/// Only shown when there is no visible candidate for completing the current argument. /// Only shown when there is no visible candidate for completing the current argument.
@ -60,6 +69,11 @@ impl CompletionCandidate {
self.help.as_ref() self.help.as_ref()
} }
/// Get the id used for de-duplicating
pub fn get_id(&self) -> Option<&String> {
self.id.as_ref()
}
/// Get the visibility of the completion candidate /// Get the visibility of the completion candidate
pub fn is_hide_set(&self) -> bool { pub fn is_hide_set(&self) -> bool {
self.hidden self.hidden

View file

@ -257,6 +257,14 @@ fn complete_arg(
if completions.iter().any(|a| !a.is_hide_set()) { if completions.iter().any(|a| !a.is_hide_set()) {
completions.retain(|a| !a.is_hide_set()); completions.retain(|a| !a.is_hide_set());
} }
let mut seen_ids = std::collections::HashSet::new();
completions.retain(move |a| {
if let Some(id) = a.get_id().cloned() {
seen_ids.insert(id)
} else {
true
}
});
Ok(completions) Ok(completions)
} }
@ -388,6 +396,7 @@ fn longs_and_visible_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
longs.into_iter().map(|s| { longs.into_iter().map(|s| {
CompletionCandidate::new(format!("--{}", s)) CompletionCandidate::new(format!("--{}", s))
.help(a.get_help().cloned()) .help(a.get_help().cloned())
.id(Some(format!("arg::{}", a.get_id())))
.hide(a.is_hide_set()) .hide(a.is_hide_set())
}) })
}) })
@ -406,6 +415,7 @@ fn hidden_longs_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
longs.into_iter().map(|s| { longs.into_iter().map(|s| {
CompletionCandidate::new(format!("--{}", s)) CompletionCandidate::new(format!("--{}", s))
.help(a.get_help().cloned()) .help(a.get_help().cloned())
.id(Some(format!("arg::{}", a.get_id())))
.hide(true) .hide(true)
}) })
}) })
@ -425,6 +435,7 @@ fn shorts_and_visible_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
shorts.into_iter().map(|s| { shorts.into_iter().map(|s| {
CompletionCandidate::new(s.to_string()) CompletionCandidate::new(s.to_string())
.help(a.get_help().cloned()) .help(a.get_help().cloned())
.id(Some(format!("arg::{}", a.get_id())))
.hide(a.is_hide_set()) .hide(a.is_hide_set())
}) })
}) })
@ -458,11 +469,13 @@ fn subcommands(p: &clap::Command) -> Vec<CompletionCandidate> {
.map(|s| { .map(|s| {
CompletionCandidate::new(s.to_string()) CompletionCandidate::new(s.to_string())
.help(sc.get_about().cloned()) .help(sc.get_about().cloned())
.id(Some(format!("command::{}", sc.get_name())))
.hide(sc.is_hide_set()) .hide(sc.is_hide_set())
}) })
.chain(sc.get_aliases().map(|s| { .chain(sc.get_aliases().map(|s| {
CompletionCandidate::new(s.to_string()) CompletionCandidate::new(s.to_string())
.help(sc.get_about().cloned()) .help(sc.get_about().cloned())
.id(Some(format!("command::{}", sc.get_name())))
.hide(true) .hide(true)
})) }))
}) })

View file

@ -255,8 +255,8 @@ fn complete_dynamic_env_toplevel() {
let input = "exhaustive \t\t"; let input = "exhaustive \t\t";
let expected = snapbox::str![[r#" let expected = snapbox::str![[r#"
% %
action help last quote --global --help -h action help last quote --global --help
alias hint pacman value --generate --version -V alias hint pacman value --generate --version
"#]]; "#]];
let actual = runtime.complete(input, &term).unwrap(); let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected); assert_data_eq!(actual, expected);
@ -275,10 +275,9 @@ fn complete_dynamic_env_quoted_help() {
let input = "exhaustive quote \t\t"; let input = "exhaustive quote \t\t";
let expected = snapbox::str![[r#" let expected = snapbox::str![[r#"
% %
cmd-backslash cmd-expansions --single-quotes --brackets --help cmd-backslash cmd-double-quotes escape-help --double-quotes --brackets --global
cmd-backticks cmd-single-quotes --double-quotes --expansions --version cmd-backticks cmd-expansions help --backticks --expansions --help
cmd-brackets escape-help --backticks --choice -h cmd-brackets cmd-single-quotes --single-quotes --backslash --choice --version
cmd-double-quotes help --backslash --global -V
"#]]; "#]];
let actual = runtime.complete(input, &term).unwrap(); let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected); assert_data_eq!(actual, expected);

View file

@ -198,8 +198,8 @@ fn complete_dynamic_env_toplevel() {
let expected = snapbox::str![[r#" let expected = snapbox::str![[r#"
% exhaustive --generate % exhaustive --generate
COMPLETING argument COMPLETING argument
--generate --help -V action help last quote --generate --help action help last quote
--global --version -h alias hint pacman value --global --version alias hint pacman value
"#]]; "#]];
let actual = runtime.complete(input, &term).unwrap(); let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected); assert_data_eq!(actual, expected);
@ -219,10 +219,9 @@ fn complete_dynamic_env_quoted_help() {
let expected = snapbox::str![[r#" let expected = snapbox::str![[r#"
% exhaustive quote --backslash % exhaustive quote --backslash
COMPLETING argument COMPLETING argument
--backslash --double-quotes --single-quotes cmd-backslash cmd-expansions --backslash --choice --global --version cmd-brackets cmd-single-quotes
--backticks --expansions --version cmd-backticks cmd-single-quotes --backticks --double-quotes --help cmd-backslash cmd-double-quotes escape-help
--brackets --global -V cmd-brackets escape-help --brackets --expansions --single-quotes cmd-backticks cmd-expansions help
--choice --help -h cmd-double-quotes help
"#]]; "#]];
let actual = runtime.complete(input, &term).unwrap(); let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected); assert_data_eq!(actual, expected);

View file

@ -75,19 +75,12 @@ fn suggest_hidden_subcommand_and_aliases() {
assert_data_eq!( assert_data_eq!(
complete!(cmd, "test"), complete!(cmd, "test"),
snapbox::str![[r#" snapbox::str!["test_visible"]
test_visible
test_visible-alias_visible
"#]]
); );
assert_data_eq!( assert_data_eq!(
complete!(cmd, "test_h"), complete!(cmd, "test_h"),
snapbox::str![[r#" snapbox::str!["test_hidden"]
test_hidden
test_hidden-alias_hidden
test_hidden-alias_visible
"#]]
); );
assert_data_eq!( assert_data_eq!(
@ -119,9 +112,7 @@ fn suggest_subcommand_aliases() {
complete!(cmd, "hello"), complete!(cmd, "hello"),
snapbox::str![[r#" snapbox::str![[r#"
hello-moon hello-moon
hello-moon-foo
hello-world hello-world
hello-world-foo
"#]], "#]],
); );
} }
@ -167,19 +158,12 @@ fn suggest_hidden_long_flag_aliases() {
assert_data_eq!( assert_data_eq!(
complete!(cmd, "--test"), complete!(cmd, "--test"),
snapbox::str![[r#" snapbox::str!["--test_visible"]
--test_visible
--test_visible-alias_visible
"#]]
); );
assert_data_eq!( assert_data_eq!(
complete!(cmd, "--test_h"), complete!(cmd, "--test_h"),
snapbox::str![[r#" snapbox::str!["--test_hidden"]
--test_hidden
--test_hidden-alias_visible
--test_hidden-alias_hidden
"#]]
); );
assert_data_eq!( assert_data_eq!(
@ -287,7 +271,6 @@ hello-world Say hello to the world
hello-moon hello-moon
goodbye-world goodbye-world
--help Print help (see more with '--help') --help Print help (see more with '--help')
-h Print help (see more with '--help')
"#]], "#]],
); );
} }
@ -361,10 +344,6 @@ pos_c
--stream --stream
--count --count
--help Print help --help Print help
-F
-S
-c
-h Print help
"#]] "#]]
); );
@ -433,9 +412,6 @@ val3
--certain-num --certain-num
--uncertain-num --uncertain-num
--help Print help --help Print help
-Y
-N
-h Print help
"#]] "#]]
); );
@ -457,9 +433,6 @@ val3
--certain-num --certain-num
--uncertain-num --uncertain-num
--help Print help --help Print help
-Y
-N
-h Print help
"#]] "#]]
); );
@ -469,9 +442,6 @@ val3
--certain-num --certain-num
--uncertain-num --uncertain-num
--help Print help --help Print help
-Y
-N
-h Print help
"#]] "#]]
); );
@ -499,9 +469,6 @@ val3
--certain-num --certain-num
--uncertain-num --uncertain-num
--help Print help --help Print help
-Y
-N
-h Print help
"#]] "#]]
); );
@ -523,9 +490,6 @@ val3
--certain-num --certain-num
--uncertain-num --uncertain-num
--help Print help --help Print help
-Y
-N
-h Print help
"#]] "#]]
); );
@ -535,9 +499,6 @@ val3
--certain-num --certain-num
--uncertain-num --uncertain-num
--help Print help --help Print help
-Y
-N
-h Print help
"#]] "#]]
); );
} }
@ -775,8 +736,6 @@ pos_b
pos_c pos_c
--format --format
--help Print help --help Print help
-F
-h Print help
"#]] "#]]
); );
@ -794,8 +753,6 @@ pos_c
snapbox::str![[r#" snapbox::str![[r#"
--format --format
--help Print help --help Print help
-F
-h Print help
"#]] "#]]
); );
@ -935,8 +892,6 @@ b_pos
c_pos c_pos
--delimiter --delimiter
--help Print help --help Print help
-D
-h Print help
"#]]); "#]]);
assert_data_eq!(complete!(cmd, " -- a_pos,[TAB]"), snapbox::str![[r#" assert_data_eq!(complete!(cmd, " -- a_pos,[TAB]"), snapbox::str![[r#"
@ -1028,16 +983,12 @@ fn suggest_positional_long_allow_hyphen() {
pos_b pos_b
--format --format
--help Print help --help Print help
-F
-h 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 pos_b
--format --format
--help Print help --help Print help
-F
-h Print help
"#]]); "#]]);
assert_data_eq!( assert_data_eq!(
@ -1076,15 +1027,11 @@ fn suggest_positional_short_allow_hyphen() {
pos_b pos_b
--format --format
--help Print help --help Print help
-F
-h 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 pos_b
--format --format
--help Print help --help Print help
-F
-h Print help
"#]]); "#]]);
assert_data_eq!( assert_data_eq!(
@ -1125,30 +1072,20 @@ pos-a
pos-b pos-b
pos-c pos-c
--required-flag --required-flag
--required-flag2
--optional-flag --optional-flag
--2optional-flag
--long-flag --long-flag
--help Print help --help Print help
-r
-o
-s -s
-h Print help
"#]] "#]]
); );
assert_data_eq!( assert_data_eq!(
complete!(cmd, "-[TAB]"), complete!(cmd, "-[TAB]"),
snapbox::str![[r#" snapbox::str![[r#"
--required-flag --required-flag
--required-flag2
--optional-flag --optional-flag
--2optional-flag
--long-flag --long-flag
--help Print help --help Print help
-r
-o
-s -s
-h Print help
"#]] "#]]
); );
} }

View file

@ -192,11 +192,10 @@ fn complete_dynamic_env_toplevel() {
let input = "exhaustive \t\t"; let input = "exhaustive \t\t";
let expected = snapbox::str![[r#" let expected = snapbox::str![[r#"
% exhaustive action % exhaustive action
action pacman --help (Print help) action last --global (everywhere)
alias quote --version (Print version) alias pacman --generate (generate)
help (Print this message or the help of the given subcommand(s)) value -h (Print help) help (Print this message or the help of the given subcommand(s)) quote --help (Print help)
hint --global (everywhere) -V (Print version) hint value --version (Print version)
last --generate (generate)
"#]]; "#]];
let actual = runtime.complete(input, &term).unwrap(); let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected); assert_data_eq!(actual, expected);
@ -215,16 +214,24 @@ fn complete_dynamic_env_quoted_help() {
let input = "exhaustive quote \t\t"; let input = "exhaustive quote \t\t";
let expected = snapbox::str![[r#" let expected = snapbox::str![[r#"
% exhaustive quote % exhaustive quote
cmd-backslash (Avoid '/n') --backticks (For more information see `echo test`) cmd-backslash (Avoid '/n')
cmd-backticks (For more information see `echo test`) --backslash (Avoid '/n') cmd-backticks (For more information see `echo test`)
cmd-brackets (List packages [filter]) --brackets (List packages [filter]) cmd-brackets (List packages [filter])
cmd-double-quotes (Can be "always", "auto", or "never") --expansions (Execute the shell command with $SHELL) cmd-double-quotes (Can be "always", "auto", or "never")
cmd-expansions (Execute the shell command with $SHELL) --choice cmd-expansions (Execute the shell command with $SHELL)
cmd-single-quotes (Can be 'always', 'auto', or 'never') --global (everywhere) cmd-single-quotes (Can be 'always', 'auto', or 'never')
escape-help (/tab "') --help (Print help (see more with '--help')) escape-help (/tab "')
help (Print this message or the help of the given subcommand(s)) --version (Print version) help (Print this message or the help of the given subcommand(s))
--single-quotes (Can be 'always', 'auto', or 'never') -h (Print help (see more with '--help')) --single-quotes (Can be 'always', 'auto', or 'never')
--double-quotes (Can be "always", "auto", or "never") -V (Print version) --double-quotes (Can be "always", "auto", or "never")
--backticks (For more information see `echo test`)
--backslash (Avoid '/n')
--brackets (List packages [filter])
--expansions (Execute the shell command with $SHELL)
--choice
--global (everywhere)
--help (Print help (see more with '--help'))
--version (Print version)
"#]]; "#]];
let actual = runtime.complete(input, &term).unwrap(); let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected); assert_data_eq!(actual, expected);

View file

@ -185,8 +185,8 @@ fn complete_dynamic_env_toplevel() {
let input = "exhaustive \t\t"; let input = "exhaustive \t\t";
let expected = snapbox::str![[r#" let expected = snapbox::str![[r#"
% exhaustive % exhaustive
--generate --help -V action help last quote --generate --help action help last quote
--global --version -h alias hint pacman value --global --version alias hint pacman value
"#]]; "#]];
let actual = runtime.complete(input, &term).unwrap(); let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected); assert_data_eq!(actual, expected);
@ -205,10 +205,9 @@ fn complete_dynamic_env_quoted_help() {
let input = "exhaustive quote \t\t"; let input = "exhaustive quote \t\t";
let expected = snapbox::str![[r#" let expected = snapbox::str![[r#"
% exhaustive quote % exhaustive quote
--backslash --double-quotes --single-quotes cmd-backslash cmd-expansions --backslash --choice --global --version cmd-brackets cmd-single-quotes
--backticks --expansions --version cmd-backticks cmd-single-quotes --backticks --double-quotes --help cmd-backslash cmd-double-quotes escape-help
--brackets --global -V cmd-brackets escape-help --brackets --expansions --single-quotes cmd-backticks cmd-expansions help
--choice --help -h cmd-double-quotes help
"#]]; "#]];
let actual = runtime.complete(input, &term).unwrap(); let actual = runtime.complete(input, &term).unwrap();
assert_data_eq!(actual, expected); assert_data_eq!(actual, expected);