Skip to content

Commit

Permalink
Allow to mark arguments as to be ignored by suggestions.
Browse files Browse the repository at this point in the history
As pointed out in clap-rs#4853, it might be useful to mark arguments to not be
considered by the suggestions feature.

In particular with the introduction of UnknownArgumentValueParser in clap-rs#5075,
one might want explicitly handle some common confusion (say handle
`--silent` and print `tip: did you mean --quiet`), but not have those in
the suggestion engine. I'm guessing one might want to have an `arg` be visible
but not in suggestion for deprecated flags as well.

I'm trying to do so by adding a `.didyoumena(false)`, that will mark the
argument as to not be considered by suggestion.

There is also some mention of completion of hidden command in clap-rs#4853,
that might be relevant as well.

--

I'm not too familiar with clap internals, and fairly new to rust,
but in particular :

 - I've tried to implement that only for args - I guess we will need to
   extend to subcommands if this goes further
 - I'm not happy with the setting negations nor the naming
   `didyoumean`/`ExcludeDidYouMean`
 - I'm not super happy about the iteration over MKeyMap keys and items.
  • Loading branch information
Carreau committed Aug 28, 2023
1 parent 7126f78 commit 49bad6c
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 6 deletions.
45 changes: 45 additions & 0 deletions clap_builder/src/builder/arg.rs
Expand Up @@ -2349,6 +2349,46 @@ impl Arg {
}
}

/// Do not include the argument in didyoumean suggestions.
///
/// # Examples
///
/// Setting `ExcludeDidYouMean` will not consider the argument
/// when making suggestions for mistyped arguments.
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, Arg};
/// let m = Command::new("prog")
/// .arg(Arg::new("cfg")
/// .long("config")
/// .didyoumean(false)
/// .get_matches_from(vec![
/// "prog", "--conf"
/// ]);
/// # }
/// ```
///
/// The above example will not display the usual `a similar argument ...`
/// message
///
/// ```text
/// error: unexpected argument '--conf' found
///
/// Usage: prog [OPTIONS]
///
/// For more information, try '--help'.
/// ```
#[inline]
#[must_use]
pub fn didyoumean(self, yes: bool) -> Self {
if yes {
self.unset_setting(ArgSettings::ExcludeDidYouMean)
} else {
self.setting(ArgSettings::ExcludeDidYouMean)
}
}

/// Do not display the [possible values][crate::builder::ValueParser::possible_values] in the help message.
///
/// This is useful for args with many values, or ones which are explained elsewhere in the
Expand Down Expand Up @@ -4153,6 +4193,11 @@ impl Arg {
self.is_set(ArgSettings::HideEnv)
}

/// dont did you mean
pub fn is_didyoumean_set(&self) -> bool {
!self.is_set(ArgSettings::ExcludeDidYouMean)
}

/// Report whether [`Arg::hide_env_values`] is set
#[cfg(feature = "env")]
pub fn is_hide_env_values_set(&self) -> bool {
Expand Down
1 change: 1 addition & 0 deletions clap_builder/src/builder/arg_settings.rs
Expand Up @@ -61,6 +61,7 @@ pub(crate) enum ArgSettings {
HiddenShortHelp,
HiddenLongHelp,
Exclusive,
ExcludeDidYouMean,
}

impl ArgSettings {
Expand Down
20 changes: 14 additions & 6 deletions clap_builder/src/parser/parser.rs
Expand Up @@ -1503,13 +1503,21 @@ impl<'cmd> Parser<'cmd> {
) -> ClapError {
debug!("Parser::did_you_mean_error: arg={arg}");
// Didn't match a flag or option
let longs = self
.cmd
.get_keymap()
let keymap = self.cmd.get_keymap();
let longs = keymap
.keys()
.filter_map(|x| match x {
KeyType::Long(l) => Some(l.to_string_lossy().into_owned()),
_ => None,
.filter_map(|key| {
let arg = keymap.get(key)?;
match (key, arg) {
(KeyType::Long(l), arg) => {
if arg.is_didyoumean_set() {
Some(l.to_string_lossy().into_owned())
} else {
None
}
}
(_, _) => None,
}
})
.collect::<Vec<_>>();
debug!("Parser::did_you_mean_error: longs={longs:?}");
Expand Down
16 changes: 16 additions & 0 deletions tests/builder/opts.rs
Expand Up @@ -458,6 +458,22 @@ For more information, try '--help'.
utils::assert_output(utils::complex_app(), "clap-test --optio=foo", DYM, true);
}

#[test]
#[cfg(feature = "suggestions")]
#[cfg(feature = "error-context")]
fn did_you_mean_disabled() {
static DYM: &str = "\
error: unexpected argument '--conf' found
Usage: clap-test [OPTIONS]
For more information, try '--help'.
";

let c = Command::new("clap-test").arg(Arg::new("cfg").long("config").didyoumean(false));
utils::assert_output(c, "clap-test --conf", DYM, true);
}

#[test]
fn issue_1047_min_zero_vals_default_val() {
let m = Command::new("foo")
Expand Down

0 comments on commit 49bad6c

Please sign in to comment.