diff --git a/clap_builder/src/output/help_template.rs b/clap_builder/src/output/help_template.rs index 9fe7211b306d..7e4dcece0eeb 100644 --- a/clap_builder/src/output/help_template.rs +++ b/clap_builder/src/output/help_template.rs @@ -684,57 +684,56 @@ impl<'cmd, 'writer> HelpTemplate<'cmd, 'writer> { let help_is_empty = help.is_empty(); self.writer.push_styled(&help); if let Some(arg) = arg { - const DASH_SPACE: usize = "- ".len(); - let possible_vals = arg.get_possible_values(); - if !possible_vals.is_empty() - && !arg.is_hide_possible_values_set() - && self.use_long_pv(arg) - { - debug!("HelpTemplate::help: Found possible vals...{possible_vals:?}"); - let longest = possible_vals - .iter() - .filter(|f| !f.is_hide_set()) - .map(|f| display_width(f.get_name())) - .max() - .expect("Only called with possible value"); - - let spaces = spaces + TAB_WIDTH - DASH_SPACE; - let trailing_indent = spaces + DASH_SPACE; - let trailing_indent = self.get_spaces(trailing_indent); - - if !help_is_empty { - let _ = write!(self.writer, "\n\n{:spaces$}", ""); - } - self.writer.push_str("Possible values:"); - for pv in possible_vals.iter().filter(|pv| !pv.is_hide_set()) { - let name = pv.get_name(); - - let mut descr = StyledStr::new(); - let _ = write!( - &mut descr, - "{}{name}{}", - literal.render(), - literal.render_reset() - ); - if let Some(help) = pv.get_help() { - debug!("HelpTemplate::help: Possible Value help"); - // To align help messages - let padding = longest - display_width(name); - let _ = write!(&mut descr, ": {:padding$}", ""); - descr.push_styled(help); + if !arg.is_hide_possible_values_set() && self.use_long_pv(arg) { + const DASH_SPACE: usize = "- ".len(); + let possible_vals = arg.get_possible_values(); + if !possible_vals.is_empty() { + debug!("HelpTemplate::help: Found possible vals...{possible_vals:?}"); + let longest = possible_vals + .iter() + .filter(|f| !f.is_hide_set()) + .map(|f| display_width(f.get_name())) + .max() + .expect("Only called with possible value"); + + let spaces = spaces + TAB_WIDTH - DASH_SPACE; + let trailing_indent = spaces + DASH_SPACE; + let trailing_indent = self.get_spaces(trailing_indent); + + if !help_is_empty { + let _ = write!(self.writer, "\n\n{:spaces$}", ""); } + self.writer.push_str("Possible values:"); + for pv in possible_vals.iter().filter(|pv| !pv.is_hide_set()) { + let name = pv.get_name(); - let avail_chars = if self.term_w > trailing_indent.len() { - self.term_w - trailing_indent.len() - } else { - usize::MAX - }; - descr.replace_newline_var(); - descr.wrap(avail_chars); - descr.indent("", &trailing_indent); - - let _ = write!(self.writer, "\n{:spaces$}- ", "",); - self.writer.push_styled(&descr); + let mut descr = StyledStr::new(); + let _ = write!( + &mut descr, + "{}{name}{}", + literal.render(), + literal.render_reset() + ); + if let Some(help) = pv.get_help() { + debug!("HelpTemplate::help: Possible Value help"); + // To align help messages + let padding = longest - display_width(name); + let _ = write!(&mut descr, ": {:padding$}", ""); + descr.push_styled(help); + } + + let avail_chars = if self.term_w > trailing_indent.len() { + self.term_w - trailing_indent.len() + } else { + usize::MAX + }; + descr.replace_newline_var(); + descr.wrap(avail_chars); + descr.indent("", &trailing_indent); + + let _ = write!(self.writer, "\n{:spaces$}- ", "",); + self.writer.push_styled(&descr); + } } } } diff --git a/tests/builder/main.rs b/tests/builder/main.rs index 22fdd5f0461c..5bd872183783 100644 --- a/tests/builder/main.rs +++ b/tests/builder/main.rs @@ -37,6 +37,7 @@ mod opts; mod positionals; mod posix_compatible; mod possible_values; +mod possible_values_expensive; mod propagate_globals; mod require; mod subcommands; diff --git a/tests/builder/possible_values_expensive.rs b/tests/builder/possible_values_expensive.rs new file mode 100644 index 000000000000..59daad17f45d --- /dev/null +++ b/tests/builder/possible_values_expensive.rs @@ -0,0 +1,118 @@ +#![cfg(feature = "string")] + +use std::{cell::RefCell, rc::Rc}; + +use clap::{Arg, Command}; +use clap_builder::builder::PossibleValuesParser; + +#[cfg(feature = "error-context")] +use super::utils; + +#[derive(Clone)] +struct ExpensiveValues { + iterated: Rc>, +} + +impl ExpensiveValues { + pub fn new() -> Self { + ExpensiveValues { + iterated: Rc::new(RefCell::new(false)), + } + } +} + +impl IntoIterator for ExpensiveValues { + type Item = String; + + type IntoIter = ExpensiveValuesIntoIterator; + + fn into_iter(self) -> Self::IntoIter { + ExpensiveValuesIntoIterator { me: self, index: 0 } + } +} + +struct ExpensiveValuesIntoIterator { + me: ExpensiveValues, + index: usize, +} + +impl Iterator for ExpensiveValuesIntoIterator { + type Item = String; + fn next(&mut self) -> Option { + *self.me.iterated.borrow_mut() = true; + + self.index += 1; + + if self.index <= 3 { + Some(format!("expensive-value-{}", self.index)) + } else { + None + } + } +} + +#[test] +fn no_iterate_when_hidden() { + static PV_EXPECTED: &str = "\ +Usage: clap-test [some-cheap-option] [some-expensive-option] + +Arguments: + [some-cheap-option] cheap [possible values: some, cheap, values] + [some-expensive-option] expensive + +Options: + -h, --help Print help +"; + let expensive = ExpensiveValues::new(); + utils::assert_output( + Command::new("test") + .arg( + Arg::new("some-cheap-option") + .help("cheap") + .value_parser(PossibleValuesParser::new(["some", "cheap", "values"])), + ) + .arg( + Arg::new("some-expensive-option") + .help("expensive") + .hide_possible_values(true) + .value_parser(PossibleValuesParser::new(expensive.clone())), + ), + "clap-test -h", + PV_EXPECTED, + false, + ); + assert_eq!(*expensive.iterated.borrow(), false); +} + +#[test] +fn iterate_when_displayed() { + static PV_EXPECTED: &str = "\ +Usage: clap-test [some-cheap-option] [some-expensive-option] + +Arguments: + [some-cheap-option] cheap [possible values: some, cheap, values] + [some-expensive-option] expensive [possible values: expensive-value-1, expensive-value-2, expensive-value-3] + +Options: + -h, --help Print help +"; + let expensive = ExpensiveValues::new(); + utils::assert_output( + Command::new("test") + .arg( + Arg::new("some-cheap-option") + .help("cheap") + .value_parser(PossibleValuesParser::new(["some", "cheap", "values"])), + ) + .arg( + Arg::new("some-expensive-option") + .help("expensive") + .hide_possible_values(false) + .value_parser(PossibleValuesParser::new(expensive.clone())), + ), + "clap-test -h", + PV_EXPECTED, + false, + ); + assert_eq!(*expensive.iterated.borrow(), true); +}