diff --git a/clap_complete/src/dynamic/complete.rs b/clap_complete/src/dynamic/complete.rs index dc2339317fb..4f1fea45ce1 100644 --- a/clap_complete/src/dynamic/complete.rs +++ b/clap_complete/src/dynamic/complete.rs @@ -262,6 +262,9 @@ fn complete_arg_value( let mut values = Vec::new(); debug!("complete_arg_value: arg={arg:?}, value={value:?}"); + let (prefix, value) = + rsplit_delimiter(value, arg.get_value_delimiter()).unwrap_or((None, value)); + let value_os = match value { Ok(value) => OsStr::new(value), Err(value_os) => value_os, @@ -316,9 +319,28 @@ fn complete_arg_value( values.sort(); } + if let Some(prefix) = prefix { + values = values + .into_iter() + .map(|comp| comp.add_prefix(prefix)) + .collect(); + } values } +fn rsplit_delimiter<'s, 'o>( + value: Result<&'s str, &'o OsStr>, + delimiter: Option, +) -> Option<(Option<&'s str>, Result<&'s str, &'o OsStr>)> { + let delimiter = delimiter?; + let value = value.ok()?; + let pos = value.rfind(delimiter)?; + let (prefix, value) = value + .split_at_checked(pos + delimiter.len_utf8()) + .expect("since delimiter was found, it is within bounds"); + Some((Some(prefix), Ok(value))) +} + fn complete_path( value_os: &OsStr, current_dir: Option<&std::path::Path>, diff --git a/clap_complete/tests/testsuite/dynamic.rs b/clap_complete/tests/testsuite/dynamic.rs index 6d8138f1352..bbde81688e1 100644 --- a/clap_complete/tests/testsuite/dynamic.rs +++ b/clap_complete/tests/testsuite/dynamic.rs @@ -615,6 +615,164 @@ pos_c ); } +#[test] +fn suggest_delimiter_values() { + let mut cmd = Command::new("delimiter") + .arg( + clap::Arg::new("delimiter") + .long("delimiter") + .short('D') + .value_parser([ + PossibleValue::new("comma"), + PossibleValue::new("space"), + PossibleValue::new("tab"), + ]) + .value_delimiter(','), + ) + .arg( + clap::Arg::new("pos") + .index(1) + .value_parser(["a_pos", "b_pos", "c_pos"]) + .value_delimiter(','), + ); + + assert_data_eq!( + complete!(cmd, "--delimiter [TAB]"), + snapbox::str![ + "comma +space +tab" + ] + ); + + assert_data_eq!( + complete!(cmd, "--delimiter=[TAB]"), + snapbox::str![ + "--delimiter=comma +--delimiter=space +--delimiter=tab" + ] + ); + + assert_data_eq!(complete!(cmd, "--delimiter c[TAB]"), snapbox::str!["comma"]); + + assert_data_eq!( + complete!(cmd, "--delimiter=c[TAB]"), + snapbox::str!["--delimiter=comma"] + ); + + assert_data_eq!( + complete!(cmd, "--delimiter comma,[TAB]"), + snapbox::str![ + "comma,comma +comma,space +comma,tab" + ] + ); + + assert_data_eq!( + complete!(cmd, "--delimiter=comma,[TAB]"), + snapbox::str![ + "--delimiter=comma,comma +--delimiter=comma,space +--delimiter=comma,tab +--delimiter=comma,a_pos +--delimiter=comma,b_pos +--delimiter=comma,c_pos" + ] + ); + + assert_data_eq!( + complete!(cmd, "--delimiter comma,s[TAB]"), + snapbox::str!["comma,space"] + ); + + assert_data_eq!( + complete!(cmd, "--delimiter=comma,s[TAB]"), + snapbox::str!["--delimiter=comma,space"] + ); + + assert_data_eq!( + complete!(cmd, "-D [TAB]"), + snapbox::str![ + "comma +space +tab" + ] + ); + + assert_data_eq!( + complete!(cmd, "-D=[TAB]"), + snapbox::str![ + "-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![ + "comma,comma +comma,space +comma,tab" + ] + ); + + assert_data_eq!( + complete!(cmd, "-D=comma,[TAB]"), + snapbox::str![ + "-D=comma,comma +-D=comma,space +-D=comma,tab +-D=comma,a_pos +-D=comma,b_pos +-D=comma,c_pos" + ] + ); + + assert_data_eq!( + complete!(cmd, "-D comma,s[TAB]"), + snapbox::str!["comma,space"] + ); + + assert_data_eq!( + complete!(cmd, "-D=comma,s[TAB]"), + snapbox::str!["-D=comma,space"] + ); + + assert_data_eq!( + complete!(cmd, "-- [TAB]"), + snapbox::str![ + "--delimiter +--help\tPrint help +-D +-h\tPrint help +a_pos +b_pos +c_pos" + ] + ); + + assert_data_eq!( + complete!(cmd, " -- a_pos,[TAB]"), + snapbox::str![ + "a_pos,a_pos +a_pos,b_pos +a_pos,c_pos" + ] + ); + + assert_data_eq!( + complete!(cmd, "-- a_pos,b[TAB]"), + snapbox::str!["a_pos,b_pos"] + ); +} + 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())];