From 1c5e4034ddf536599e51febd485c9aba79d27e28 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 9 Apr 2025 14:41:10 +0800 Subject: [PATCH] Add usage_column and usage_width --- src/lib.rs | 56 +++++++++++++++++++++++++++++++++++--- src/tests/mod.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f5c8e3cf..80d4cef8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,6 +142,8 @@ pub struct Options { grps: Vec, parsing_style: ParsingStyle, long_only: bool, + usage_column: usize, + usage_width: usize, } impl Default for Options { @@ -157,6 +159,8 @@ impl Options { grps: Vec::new(), parsing_style: ParsingStyle::FloatingFrees, long_only: false, + usage_column: 24, + usage_width: 54, } } @@ -618,7 +622,10 @@ impl Options { /// Derive usage items from a set of options. fn usage_items<'a>(&'a self) -> Box + 'a> { - let desc_sep = format!("\n{}", repeat(" ").take(24).collect::()); + let desc_sep = format!( + "\n{}", + repeat(" ").take(self.usage_column).collect::() + ); let any_short = self.grps.iter().any(|optref| !optref.short_name.is_empty()); @@ -678,15 +685,15 @@ impl Options { } let rowlen = row.width(); - if rowlen < 24 { - for _ in 0..24 - rowlen { + if rowlen < self.usage_column { + for _ in 0..self.usage_column - rowlen { row.push(' '); } } else { row.push_str(&desc_sep) } - let desc_rows = each_split_within(&desc, 54); + let desc_rows = each_split_within(&desc, self.usage_width); row.push_str(&desc_rows.join(&desc_sep)); row @@ -694,6 +701,47 @@ impl Options { Box::new(rows) } + + /// Get the display column of usage, see [`Options::set_usage_column`] for details + pub fn usage_column(&self) -> usize { + self.usage_column.clone() + } + + /// Set the display column of usage + /// + /// # Example + /// + /// ```text + /// Options: + /// -h, --help show help + /// |<-------------------->| + /// usage_column + /// ``` + pub fn set_usage_column(&mut self, column: usize) -> &mut Options { + self.usage_column = column; + self + } + + /// Get the display width of usage, see [`Options::set_usage_width`] for details + pub fn usage_width(&self) -> usize { + self.usage_width.clone() + } + + /// Set the display width of usage + /// + /// # Example + /// + /// ```text + /// Options: + /// -h, --help This description too long and will be split into new + /// line. + /// |<-------------------------------------------------->| + /// usage_width + /// ``` + pub fn set_usage_width(&mut self, width: usize) -> &mut Options { + self.usage_width = width; + self + } } fn validate_names(short_name: &str, long_name: &str) { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 17e689c4..054aaf63 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -919,6 +919,41 @@ Options: assert!(usage == expected) } +#[test] +#[cfg(feature = "unicode")] +fn test_usage_description_newline_handling_custom_width() { + let mut opts = Options::new(); + opts.set_usage_width(30); + opts.optflag( + "k", + "k\u{2013}w\u{2013}", + "The word kiwi is normally spelled with two i's", + ); + opts.optflag( + "a", + "apple", + "This description forces a new line.\n Here is a premature\n\ + newline", + ); + + let expected = "Usage: fruits + +Options: + -k, --k–w– The word kiwi is normally + spelled with two i's + -a, --apple This description forces a new + line. + Here is a premature + newline +"; + + let usage = opts.usage("Usage: fruits"); + + debug!("expected: <<{}>>", expected); + debug!("generated: <<{}>>", usage); + assert!(usage == expected) +} + #[test] #[cfg(feature = "unicode")] fn test_usage_description_newline_handling() { @@ -951,6 +986,41 @@ Options: assert!(usage == expected) } +#[test] +#[cfg(feature = "unicode")] +fn test_usage_description_newline_handling_custom_column() { + let mut opts = Options::new(); + opts.set_usage_column(10); + opts.optflag( + "k", + "k\u{2013}w\u{2013}", + "The word kiwi is normally spelled with two i's", + ); + opts.optflag( + "a", + "apple", + "This description forces a new line.\n Here is a premature\n\ + newline", + ); + + let expected = "Usage: fruits + +Options: + -k, --k–w–\u{0020} + The word kiwi is normally spelled with two i's + -a, --apple\u{0020} + This description forces a new line. + Here is a premature + newline +"; + + let usage = opts.usage("Usage: fruits"); + + debug!("expected: <<{}>>", expected); + debug!("generated: <<{}>>", usage); + assert!(usage == expected) +} + #[test] #[cfg(feature = "unicode")] fn test_usage_multiwidth() {