Skip to content

Commit

Permalink
feat: Add Visible Aliases for PossibleValue
Browse files Browse the repository at this point in the history
Before this change, it was not possible to have *visible* aliases
for PossibleValues, all aliases were hidden in help output. This
change adds a new API for adding visible aliases and modifies the
help generation scheme to display these visible aliases.
  • Loading branch information
naftulikay committed May 1, 2024
1 parent 43efde9 commit 67df90a
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 19 deletions.
56 changes: 53 additions & 3 deletions clap_builder/src/builder/possible_value.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use anstyle::Style;

use crate::builder::IntoResettable;
use crate::builder::Str;
use crate::builder::StyledStr;
Expand Down Expand Up @@ -37,6 +39,7 @@ pub struct PossibleValue {
name: Str,
help: Option<StyledStr>,
aliases: Vec<Str>, // (name, visible)
visible_aliases: Vec<Str>,
hide: bool,
}

Expand Down Expand Up @@ -130,6 +133,18 @@ impl PossibleValue {
self
}

/// FIXME
#[must_use]
pub fn visible_alias(mut self, name: impl IntoResettable<Str>) -> Self {
if let Some(name) = name.into_resettable().into_option() {
self.visible_aliases.push(name);
} else {
self.visible_aliases.clear();
}

self
}

/// Sets multiple *hidden* aliases for this argument value.
///
/// # Examples
Expand All @@ -142,10 +157,18 @@ impl PossibleValue {
/// # ;
/// ```
#[must_use]
pub fn aliases(mut self, names: impl IntoIterator<Item = impl Into<Str>>) -> Self {
pub fn aliases(mut self, names: impl IntoIterator<Item=impl Into<Str>>) -> Self {
self.aliases.extend(names.into_iter().map(|a| a.into()));
self
}

/// FIXME
#[must_use]
pub fn visible_aliases(mut self, names: impl IntoIterator<Item=impl Into<Str>>) -> Self {
self.visible_aliases
.extend(names.into_iter().map(|a| a.into()));
self
}
}

/// Reflection
Expand All @@ -156,6 +179,33 @@ impl PossibleValue {
self.name.as_str()
}

/// Get the visible aliases for this argument
pub fn get_visible_aliases(&self) -> &Vec<Str> {
&self.visible_aliases
}

/// Render help text for this [PossibleValue] with all of its visible aliases included.
///
/// If there are no visible aliases, this will simply emit the formatted name, if there are visible aliases, these
/// will be appended like this: `name [aliases: a, b, c]`.
pub fn render_help_prefix_long(&self, literal: &Style) -> String {
const ALIASES_PREFIX: &str = " [aliases: ";
const ALIASES_POSTFIX: &str = "]";
const ALIASES_JOINER: &str = ", ";

let name = self.get_name();
let aliases = if self.get_visible_aliases().is_empty() {
"".to_string()
} else {
// [aliases: a, b, c]
format!("{ALIASES_PREFIX}{}{ALIASES_POSTFIX}", self.get_visible_aliases().iter().map(|s| {
format!("{}{s}{}", literal.render(), literal.render_reset())
}).collect::<Vec<_>>().join(ALIASES_JOINER))
};

format!("{}{name}{}{aliases}", literal.render(), literal.render_reset())
}

/// Get the help specified for this argument, if any
#[inline]
pub fn get_help(&self) -> Option<&StyledStr> {
Expand Down Expand Up @@ -191,8 +241,8 @@ impl PossibleValue {
/// 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()))
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())).chain(self.visible_aliases.iter().map(|s| s.as_str()))
}

/// Tests if the value is valid for this argument value
Expand Down
36 changes: 20 additions & 16 deletions clap_builder/src/output/help_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ use std::borrow::Cow;
use std::cmp;
use std::usize;

use crate::builder::{Arg, Command};
// Internal
use crate::builder::PossibleValue;
use crate::builder::Str;
use crate::builder::StyledStr;
use crate::builder::Styles;
use crate::builder::{Arg, Command};
use crate::output::display_width;
use crate::output::wrap;
use crate::output::Usage;
use crate::output::TAB;
use crate::output::TAB_WIDTH;
use crate::output::Usage;
use crate::output::wrap;
use crate::util::FlatSet;

/// `clap` auto-generated help writer
Expand Down Expand Up @@ -689,10 +689,12 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
let possible_vals = arg.get_possible_values();
if !possible_vals.is_empty() {
debug!("HelpTemplate::help: Found possible vals...{possible_vals:?}");

// FIXME
let longest = possible_vals
.iter()
.filter(|f| !f.is_hide_set())
.map(|f| display_width(f.get_name()))
.map(|f| display_width(f.render_help_prefix_long(literal).as_str()))
.max()
.expect("Only called with possible value");

Expand All @@ -705,19 +707,18 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
}
self.writer.push_str("Possible values:");
for pv in possible_vals.iter().filter(|pv| !pv.is_hide_set()) {
let name = pv.get_name();
let prefix = pv.render_help_prefix_long(literal);

let mut descr = StyledStr::new();

let _ = write!(
&mut descr,
"{}{name}{}",
literal.render(),
literal.render_reset()
"{prefix}",
);
if let Some(help) = pv.get_help() {
debug!("HelpTemplate::help: Possible Value help");
// To align help messages
let padding = longest - display_width(name);
let padding = longest - display_width(prefix.as_str());
let _ = write!(&mut descr, ": {:padding$}", "");
descr.push_styled(help);
}
Expand All @@ -731,7 +732,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
descr.wrap(avail_chars);
descr.indent("", &trailing_indent);

let _ = write!(self.writer, "\n{:spaces$}- ", "",);
let _ = write!(self.writer, "\n{:spaces$}- ", "", );
self.writer.push_styled(&descr);
}
}
Expand Down Expand Up @@ -848,9 +849,12 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
if !possible_vals.is_empty() {
debug!("HelpTemplate::spec_vals: Found possible vals...{possible_vals:?}");

// FIXME here is where possible values are formatted in short form, should be a list with no difference
// between possible value names and visible aliases
let pvs = possible_vals
.iter()
.filter_map(PossibleValue::get_visible_quoted_name)
.filter(|v| !v.is_hide_set())
.flat_map(|v| std::iter::once(v.get_name()).chain(v.get_visible_aliases().iter().map(|s| s.as_str())))
.collect::<Vec<_>>()
.join(", ");

Expand All @@ -873,9 +877,9 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
fn use_long_pv(&self, arg: &Arg) -> bool {
self.use_long
&& arg
.get_possible_values()
.iter()
.any(PossibleValue::should_show_help)
.get_possible_values()
.iter()
.any(PossibleValue::should_show_help)
}
}

Expand Down Expand Up @@ -922,7 +926,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
header.render_reset()
);
if !about.is_empty() {
let _ = write!(self.writer, "{about}\n",);
let _ = write!(self.writer, "{about}\n", );
}

let mut sub_help = HelpTemplate {
Expand Down Expand Up @@ -1002,7 +1006,7 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> {
/// Will use next line help on writing subcommands.
fn will_subcommands_wrap<'a>(
&self,
subcommands: impl IntoIterator<Item = &'a Command>,
subcommands: impl IntoIterator<Item=&'a Command>,
longest: usize,
) -> bool {
subcommands
Expand Down

0 comments on commit 67df90a

Please sign in to comment.