Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions clap_complete/src/engine/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ pub fn complete(
});

if let Some(opt) = opt {
if opt.get_num_args().expect("built").takes_values() && value.is_none() {
if opt.get_num_args().expect("built").takes_values()
&& value.is_none()
&& !opt.is_require_equals_set()
{
Comment on lines +91 to +94
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is for

No longer suggests values after --flag (space-separated), since the parser would reject that form

The intention might not be obvious in the code. Would be good to find a way to make this clear

next_state = ParseState::Opt((opt, 1));
};
} else if pos_allows_hyphen(current_cmd, pos_index) {
Expand Down Expand Up @@ -478,9 +481,16 @@ fn longs_and_visible_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
p.get_arguments()
.filter_map(|a| {
a.get_long_and_visible_aliases().map(|longs| {
longs
.into_iter()
.map(|s| populate_arg_candidate(CompletionCandidate::new(format!("--{s}")), a))
longs.into_iter().map(|s| {
let suffix = if a.is_require_equals_set()
&& a.get_num_args().expect("built").takes_values()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

takes_values is redundant

{
"="
} else {
""
};
populate_arg_candidate(CompletionCandidate::new(format!("--{s}{suffix}")), a)
})
})
})
.flatten()
Expand All @@ -495,7 +505,15 @@ fn hidden_longs_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
.filter_map(|a| {
a.get_aliases().map(|longs| {
longs.into_iter().map(|s| {
populate_arg_candidate(CompletionCandidate::new(format!("--{s}")), a).hide(true)
let suffix = if a.is_require_equals_set()
&& a.get_num_args().expect("built").takes_values()
{
"="
} else {
""
};
populate_arg_candidate(CompletionCandidate::new(format!("--{s}{suffix}")), a)
.hide(true)
})
})
})
Expand Down
52 changes: 52 additions & 0 deletions clap_complete/tests/testsuite/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,58 @@ pos-c
);
}

#[test]
fn suggest_require_equals() {
let mut cmd = Command::new("exhaustive")
.arg(
clap::Arg::new("format")
.long("format")
.require_equals(true)
.value_parser(["json", "yaml", "toml"]),
)
.arg(
clap::Arg::new("name")
.long("name")
.short('n'),
);

Comment on lines +1447 to +1460
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a test case with num_args(0..=1) like at https://docs.rs/clap/latest/src/git/git.rs.html#25-31 which is a major use case for this feature

// When completing after an empty input, --format should appear with = appended
assert_data_eq!(
complete!(cmd, " [TAB]"),
snapbox::str![[r#"
--format=
--name
--help Print help
"#]]
);

// Should complete values after --format=
assert_data_eq!(
complete!(cmd, "--format=[TAB]"),
snapbox::str![[r#"
--format=json
--format=yaml
--format=toml
"#]]
);

// Should complete values after --format=j
assert_data_eq!(
complete!(cmd, "--format=j[TAB]"),
snapbox::str!["--format=json"]
);

// When typing --format (space), should NOT suggest values since require_equals forbids it
assert_data_eq!(
complete!(cmd, "--format [TAB]"),
snapbox::str![[r#"
--format=
--name
--help Print help
"#]]
);
}

fn complete(cmd: &mut Command, args: impl AsRef<str>, current_dir: Option<&Path>) -> String {
let input = args.as_ref();
let mut args = vec![std::ffi::OsString::from(cmd.get_name())];
Expand Down
Loading