-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Support visible aliases for possible values #4344
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,7 @@ use crate::util::eq_ignore_case; | |
pub struct PossibleValue { | ||
name: Str, | ||
help: Option<StyledStr>, | ||
aliases: Vec<Str>, // (name, visible) | ||
aliases: Vec<(Str, bool)>, // (name, visible) | ||
hide: bool, | ||
} | ||
|
||
|
@@ -114,7 +114,7 @@ impl PossibleValue { | |
#[must_use] | ||
pub fn alias(mut self, name: impl IntoResettable<Str>) -> Self { | ||
if let Some(name) = name.into_resettable().into_option() { | ||
self.aliases.push(name); | ||
self.aliases.push((name, false)); | ||
} else { | ||
self.aliases.clear(); | ||
} | ||
|
@@ -133,7 +133,45 @@ impl PossibleValue { | |
/// ``` | ||
#[must_use] | ||
pub fn aliases(mut self, names: impl IntoIterator<Item = impl Into<Str>>) -> Self { | ||
self.aliases.extend(names.into_iter().map(|a| a.into())); | ||
self.aliases | ||
.extend(names.into_iter().map(|a| (a.into(), false))); | ||
self | ||
} | ||
|
||
/// Sets a *visible* alias for this argument value. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ```rust | ||
/// # use clap::builder::PossibleValue; | ||
/// PossibleValue::new("slow") | ||
/// .visible_alias("not-fast") | ||
/// # ; | ||
/// ``` | ||
#[must_use] | ||
pub fn visible_alias(mut self, name: impl IntoResettable<Str>) -> Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Whats holding that back? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @epage Primarily, the amount of additional complexity required to pass groups of aliases through the error-context infrastructure and update all the places creating those. Secondarily, trying to figure out how to handle the "did you mean". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So long as we hide visible aliases in the short help, we can forego them in the errors. For the suggestion,
I can go either way btw can you update the commit with some of these "for now" decisions so its easier for someone to discover them when making changes in the future? People might discover the commit but its then a smaller set of people who might explore the PR comments. |
||
if let Some(name) = name.into_resettable().into_option() { | ||
self.aliases.push((name, true)); | ||
} else { | ||
self.aliases.clear(); | ||
} | ||
self | ||
} | ||
|
||
/// Sets multiple *visible* aliases for this argument value. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ```rust | ||
/// # use clap::builder::PossibleValue; | ||
/// PossibleValue::new("slow") | ||
/// .visible_aliases(["not-fast", "snake-like"]) | ||
/// # ; | ||
/// ``` | ||
#[must_use] | ||
pub fn visible_aliases(mut self, names: impl IntoIterator<Item = impl Into<Str>>) -> Self { | ||
self.aliases | ||
.extend(names.into_iter().map(|a| (a.into(), true))); | ||
self | ||
} | ||
} | ||
|
@@ -180,21 +218,25 @@ impl PossibleValue { | |
#[cfg(feature = "help")] | ||
pub(crate) fn get_visible_quoted_name(&self) -> Option<std::borrow::Cow<'_, str>> { | ||
if !self.hide { | ||
Some(if self.name.contains(char::is_whitespace) { | ||
format!("{:?}", self.name).into() | ||
} else { | ||
self.name.as_str().into() | ||
}) | ||
Some(quote_if_whitespace(&self.name)) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Get the names of all visible aliases, but wrapped in quotes if they contain whitespace | ||
pub fn get_quoted_visible_aliases(&self) -> Vec<std::borrow::Cow<'_, str>> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels too specialized to make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @epage There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @epage What would you suggest instead? I don't want to leave them out of the manpages entirely. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate what you need |
||
self.aliases | ||
.iter() | ||
.filter_map(|(n, vis)| vis.then(|| quote_if_whitespace(n))) | ||
.collect() | ||
} | ||
|
||
/// Returns all valid values of the argument value. | ||
/// | ||
/// Namely the name and all aliases. | ||
pub fn get_name_and_aliases(&self) -> impl Iterator<Item = &str> + '_ { | ||
std::iter::once(self.get_name()).chain(self.aliases.iter().map(|s| s.as_str())) | ||
std::iter::once(self.get_name()).chain(self.aliases.iter().map(|(s, _)| s.as_str())) | ||
} | ||
|
||
/// Tests if the value is valid for this argument value | ||
|
@@ -205,9 +247,10 @@ impl PossibleValue { | |
/// | ||
/// ```rust | ||
/// # use clap::builder::PossibleValue; | ||
/// let arg_value = PossibleValue::new("fast").alias("not-slow"); | ||
/// let arg_value = PossibleValue::new("fast").visible_alias("quick").alias("not-slow"); | ||
/// | ||
/// assert!(arg_value.matches("fast", false)); | ||
/// assert!(arg_value.matches("quick", false)); | ||
/// assert!(arg_value.matches("not-slow", false)); | ||
/// | ||
/// assert!(arg_value.matches("FAST", true)); | ||
|
@@ -223,6 +266,15 @@ impl PossibleValue { | |
} | ||
} | ||
|
||
/// Quote a string if it contains whitespace | ||
fn quote_if_whitespace(n: &Str) -> std::borrow::Cow<'_, str> { | ||
if n.contains(char::is_whitespace) { | ||
format!("{:?}", n).into() | ||
} else { | ||
n.as_str().into() | ||
} | ||
} | ||
|
||
impl<S: Into<Str>> From<S> for PossibleValue { | ||
fn from(s: S) -> Self { | ||
Self::new(s) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel like this is too many layers of parenthetical statements.
We should either flatten them or reserve them for long help.
I lean towards flattening.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@epage I think it would be confusing to put all the aliases into a single flat list, without indicating what value they're aliases for.
No objections to only showing them in long help, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is more being driven by your needs, I'll defer to your preference until we get wider feedback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@epage FWIW, while I have a need for visible aliases, I have no particular preferences regarding how to display them, as long as they're displayed and it's clear what they're an alias for. I do think the brief help may just be too short to reasonably show them.