diff --git a/clap_complete/src/engine/complete.rs b/clap_complete/src/engine/complete.rs index f6e638d8636..b19adaf808a 100644 --- a/clap_complete/src/engine/complete.rs +++ b/clap_complete/src/engine/complete.rs @@ -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() + { next_state = ParseState::Opt((opt, 1)); }; } else if pos_allows_hyphen(current_cmd, pos_index) { @@ -478,9 +481,16 @@ fn longs_and_visible_aliases(p: &clap::Command) -> Vec { 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() + { + "=" + } else { + "" + }; + populate_arg_candidate(CompletionCandidate::new(format!("--{s}{suffix}")), a) + }) }) }) .flatten() @@ -495,7 +505,15 @@ fn hidden_longs_aliases(p: &clap::Command) -> Vec { .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) }) }) }) diff --git a/clap_complete/tests/testsuite/engine.rs b/clap_complete/tests/testsuite/engine.rs index b8f03ee07e2..8c14720021a 100644 --- a/clap_complete/tests/testsuite/engine.rs +++ b/clap_complete/tests/testsuite/engine.rs @@ -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'), + ); + + // 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, current_dir: Option<&Path>) -> String { let input = args.as_ref(); let mut args = vec![std::ffi::OsString::from(cmd.get_name())];