diff --git a/clap_complete/src/dynamic/completer.rs b/clap_complete/src/dynamic/completer.rs index 5f540c3d16a..d05e00fef3a 100644 --- a/clap_complete/src/dynamic/completer.rs +++ b/clap_complete/src/dynamic/completer.rs @@ -242,7 +242,7 @@ fn complete_arg( completions.extend(hidden_longs_aliases(cmd)); } - if arg.is_empty() || arg.is_stdio() || arg.is_short() { + if arg.is_empty() || arg.is_stdio() { let dash_or_arg = if arg.is_empty() { "-".into() } else { @@ -263,6 +263,86 @@ fn complete_arg( .visible(true) }), ); + } else if let Some(mut short) = arg.to_short() { + if !short.is_negative_number() { + let takes_value_opt; + let mut leading_flags = OsString::new(); + // Find the first takes_value option. + loop { + match short.next_flag() { + Some(Ok(opt)) => { + leading_flags.push(opt.to_string()); + let opt = cmd.get_arguments().find(|a| { + let shorts = a.get_short_and_visible_aliases(); + let is_find = shorts.map(|v| { + let mut iter = v.into_iter(); + let c = iter.find(|c| *c == opt); + c.is_some() + }); + is_find.unwrap_or(false) + }); + match opt.map(|o| o.get_action()) { + Some(clap::ArgAction::Set) | Some(clap::ArgAction::Append) => { + takes_value_opt = opt; + break; + } + Some(clap::ArgAction::SetTrue) + | Some(clap::ArgAction::SetFalse) + | Some(clap::ArgAction::Count) + | Some(clap::ArgAction::Version) + | Some(clap::ArgAction::Help) + | Some(clap::ArgAction::HelpShort) + | Some(clap::ArgAction::HelpLong) => (), + Some(_) => (), + None => (), + } + } + Some(Err(_)) | None => { + takes_value_opt = None; + break; + } + } + } + + // Clone `short` to `peek_short` to peek whether the next flag is a `=`. + if let Some(opt) = takes_value_opt { + let mut peek_short = short.clone(); + let has_equal = if let Some(Ok('=')) = peek_short.next_flag() { + short.next_flag(); + true + } else { + false + }; + + let value = short.next_value_os().unwrap_or(OsStr::new("")); + completions.extend( + complete_arg_value(value.to_str().ok_or(value), opt, current_dir) + .into_iter() + .map(|comp| { + CompletionCandidate::new(format!( + "-{}{}{}", + leading_flags.to_string_lossy(), + if has_equal { "=" } else { "" }, + comp.get_content().to_string_lossy() + )) + .help(comp.get_help().cloned()) + .visible(comp.is_visible()) + }), + ); + } else { + completions.extend(shorts_and_visible_aliases(cmd).into_iter().map( + |comp| { + CompletionCandidate::new(format!( + "-{}{}", + leading_flags.to_string_lossy(), + comp.get_content().to_string_lossy() + )) + .help(comp.get_help().cloned()) + .visible(comp.is_visible()) + }, + )); + } + } } if let Some(positional) = cmd diff --git a/clap_complete/tests/testsuite/dynamic.rs b/clap_complete/tests/testsuite/dynamic.rs index 0ff3aba1436..81057cd55d3 100644 --- a/clap_complete/tests/testsuite/dynamic.rs +++ b/clap_complete/tests/testsuite/dynamic.rs @@ -406,51 +406,36 @@ pos_c" assert_data_eq!( complete!(cmd, "-ci[TAB]", current_dir = Some(testdir_path)), snapbox::str![ - "-cii --ciF --cic --cih Print help" + "-cia_file +-cib_file +-cic_dir/ +-cid_dir/" ] ); assert_data_eq!( complete!(cmd, "-ci=[TAB]", current_dir = Some(testdir_path)), snapbox::str![ - "-ci=i --ci=F --ci=c --ci=h Print help" + "-ci=a_file +-ci=b_file +-ci=c_dir/ +-ci=d_dir/" ] ); assert_data_eq!( complete!(cmd, "-ci=a[TAB]", current_dir = Some(testdir_path)), - snapbox::str![ - "-ci=ai --ci=aF --ci=ac --ci=ah Print help" - ] + snapbox::str!["-ci=a_file"] ); assert_data_eq!( complete!(cmd, "-ciF[TAB]", current_dir = Some(testdir_path)), - snapbox::str![ - "-ciFi --ciFF --ciFc --ciFh\tPrint help" - ] + snapbox::str![""] ); assert_data_eq!( complete!(cmd, "-ciF=[TAB]", current_dir = Some(testdir_path)), - snapbox::str![ - "-ciF=i --ciF=F --ciF=c --ciF=h\tPrint help" - ] + snapbox::str![""] ) }