Repository: clap-rs/clap
Pull request #4258 (2022-09-26 16:39:46)

fix(help)!: Wrapping is behind wrap_help 
If users don't want `wrap_help` feature, they can put newlines in where
needed.  Seems odd to support wrapping when the wrap size is fixed.  For
those hard coding the lines, this will save them a decent amount of
size.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 064500b8..f4a810e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -125,9 +125,12 @@ Steps:
 2. *If using Builder API*: Explicitly set the `arg.action(ArgAction::...)` on each argument (`StoreValue` for options and `IncOccurrences` for flags)
 3. Run `cargo check --features clap/deprecated` and resolve all deprecation warnings
 4. Upgrade to v4
-5. Resolve compiler errors
-6. Resolve behavior changes (see "subtle changes" under BREAKING CHANGES)
-7. *At your leisure:* resolve new deprecation notices
+5. Update feature flags
+  - *If `default-features = false`*, run `cargo add clap -F help,usage,error-context`
+  -  Run `cargo add clap -F wrap_help` unless you want to hard code line wraps
+6. Resolve compiler errors
+7. Resolve behavior changes (see "subtle changes" under BREAKING CHANGES)
+8. *At your leisure:* resolve new deprecation notices
 
 Example test (derive):
 ```rust
@@ -155,7 +158,7 @@ fn verify_cli() {
 }
 ```
 
-Note: the idomatic / recommended way of specifying different types of args in the Builder API has changed:
+Note: the idiomatic / recommended way of specifying different types of args in the Builder API has changed:
 
 Before
 ```rust
@@ -184,10 +187,11 @@ Subtle changes (i.e. compiler won't catch):
 - `mut_arg` can no longer be used to customize help and version arguments, instead disable them (`Command::disable_help_flag`, `Command::disable_version_flag`) and provide your own (#4056)
 - Removed lifetimes from `Command`, `Arg`, `ArgGroup`, and `PossibleValue`, assuming `'static`.  `string` feature flag will enable support for `String`s (#1041, #2150, #4223)
 - `arg!(--flag <value>)` is now optional, instead of required.  Add `.required(true)` at the end to restore the original behavior (#4206)
-- Added default feature flags, `help`, `usage` and `error-context` (#4236)
+- Added default feature flags, `help`, `usage` and `error-context`, requiring adding them back in if `default-features = false` (#4236)
 - *(parser)* Always fill in `""` argument for external subcommands to make it easier to distinguish them from built-in commands (#3263)
 - *(parser)* Short flags now have higher precedence than hyphen values with `Arg::allow_hyphen_values`, to be consistent with `Command::allow_hyphen_values` (#4187)
 - *(parser)* `Arg::value_terminator` must be its own argument on the CLI rather than being in a delimited list (#4025)
+- *(help)* Line wrapping of help is now behind the existing `wrap_help` feature flag, either enable it or hard code your wraps (#4258)
 - *(help)* Make `DeriveDisplayOrder` the default and removed the setting.  To sort help, set `next_display_order(None)` (#2808)
 - *(help)* Subcommand display order respects `Command::next_display_order` instead of `DeriveDisplayOrder` and using its own initial display order value (#2808)
 - *(help)* Subcommands are now listed before arguments.  To get the old behavior, see `Command::help_template` (#4132)
diff --git a/Cargo.toml b/Cargo.toml
index 771c5c96..9c4ac31f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -77,7 +77,7 @@ suggestions = ["dep:strsim", "error-context"]
 deprecated = ["clap_derive?/deprecated"] # Guided experience to prepare for next breaking release (at different stages of development, this may become default)
 derive = ["clap_derive", "dep:once_cell"]
 cargo = ["dep:once_cell"] # Disable if you're not using Cargo, enables Cargo-env-var-dependent macros
-wrap_help = ["dep:terminal_size"]
+wrap_help = ["help", "dep:terminal_size"]
 env = [] # Use environment variables during arg parsing
 unicode = ["dep:unicode-width", "dep:unicase"]  # Support for unicode characters in arguments and help messages
 string = []  # Allow runtime generated strings
diff --git a/src/_features.rs b/src/_features.rs
index 12d7e536..10d6163c 100644
--- a/src/_features.rs
+++ b/src/_features.rs
@@ -19,7 +19,7 @@
 //! * **env**: Turns on the usage of environment variables during parsing.
 //! * **unicode**: Turns on support for unicode characters (including emoji) in arguments and help messages.
 //! * **wrap_help**: Turns on the help text wrapping feature, based on the terminal size.
-//! * **string**: Allow runtime generated strings
+//! * **string**: Allow runtime generated strings (e.g. with [`Str`][crate::builder::Str]).
 //!
 //! #### Experimental features
 //!
diff --git a/src/builder/os_str.rs b/src/builder/os_str.rs
index f379b415..ba8cd231 100644
--- a/src/builder/os_str.rs
+++ b/src/builder/os_str.rs
@@ -1,6 +1,9 @@
 use crate::builder::Str;
 
 /// A UTF-8-encoded fixed string
+///
+/// **NOTE:** To support dynamic values (i.e. `OsString`), enable the [`string`
+/// feature][crate::_features]
 #[derive(Default, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
 pub struct OsStr {
     name: Inner,
diff --git a/src/builder/str.rs b/src/builder/str.rs
index e4750c74..75da8f08 100644
--- a/src/builder/str.rs
+++ b/src/builder/str.rs
@@ -1,4 +1,7 @@
 /// A UTF-8-encoded fixed string
+///
+/// **NOTE:** To support dynamic values (i.e. `String`), enable the [`string`
+/// feature][crate::_features]
 #[derive(Default, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
 pub struct Str {
     name: Inner,
diff --git a/src/builder/styled_str.rs b/src/builder/styled_str.rs
index 01017e0f..b127f0e5 100644
--- a/src/builder/styled_str.rs
+++ b/src/builder/styled_str.rs
@@ -101,7 +101,10 @@ impl StyledStr {
         }
     }
 
-    #[cfg(feature = "help")]
+    #[cfg(all(not(feature = "wrap_help"), feature = "help"))]
+    pub(crate) fn wrap(&mut self, _hard_width: usize) {}
+
+    #[cfg(feature = "wrap_help")]
     pub(crate) fn wrap(&mut self, hard_width: usize) {
         let mut wrapper = crate::output::textwrap::wrap_algorithms::LineWrapper::new(hard_width);
         for (_, content) in self.iter_mut() {
diff --git a/src/output/help_template.rs b/src/output/help_template.rs
index da16f903..00516bc5 100644
--- a/src/output/help_template.rs
+++ b/src/output/help_template.rs
@@ -1020,10 +1020,11 @@ fn longest_filter(arg: &Arg) -> bool {
 
 #[cfg(test)]
 mod test {
-    use super::*;
-
     #[test]
+    #[cfg(feature = "wrap_help")]
     fn wrap_help_last_word() {
+        use super::*;
+
         let help = String::from("foo bar baz");
         assert_eq!(wrap(&help, 5), "foo\nbar\nbaz");
     }
@@ -1031,6 +1032,8 @@ mod test {
     #[test]
     #[cfg(feature = "unicode")]
     fn display_width_handles_non_ascii() {
+        use super::*;
+
         // Popular Danish tongue-twister, the name of a fruit dessert.
         let text = "rødgrød med fløde";
         assert_eq!(display_width(text), 17);
@@ -1042,6 +1045,8 @@ mod test {
     #[test]
     #[cfg(feature = "unicode")]
     fn display_width_handles_emojis() {
+        use super::*;
+
         let text = "😂";
         // There is a single `char`...
         assert_eq!(text.chars().count(), 1);
diff --git a/src/output/textwrap/mod.rs b/src/output/textwrap/mod.rs
index c6e831f6..d14d3fe7 100644
--- a/src/output/textwrap/mod.rs
+++ b/src/output/textwrap/mod.rs
@@ -5,9 +5,12 @@
 //! - `LineWrapper` is able to incrementally wrap which will help with `StyledStr
 
 pub(crate) mod core;
+#[cfg(feature = "wrap_help")]
 pub(crate) mod word_separators;
+#[cfg(feature = "wrap_help")]
 pub(crate) mod wrap_algorithms;
 
+#[cfg(feature = "wrap_help")]
 pub(crate) fn wrap(content: &str, hard_width: usize) -> String {
     let mut wrapper = wrap_algorithms::LineWrapper::new(hard_width);
     let mut total = Vec::new();
@@ -19,7 +22,13 @@ pub(crate) fn wrap(content: &str, hard_width: usize) -> String {
     total.join("")
 }
 
+#[cfg(not(feature = "wrap_help"))]
+pub(crate) fn wrap(content: &str, _hard_width: usize) -> String {
+    content.to_owned()
+}
+
 #[cfg(test)]
+#[cfg(feature = "wrap_help")]
 mod test {
     /// Compatibility shim to keep textwrap's tests
     fn wrap(content: &str, hard_width: usize) -> Vec<String> {


Test patch:
diff --git a/tests/builder/help.rs b/tests/builder/help.rs
index 2255b011..1bd8a286 100644
--- a/tests/builder/help.rs
+++ b/tests/builder/help.rs
@@ -531,6 +531,7 @@ Options:
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn issue_626_unicode_cutoff() {
     static ISSUE_626_CUTOFF: &str = "\
 Usage: ctest [OPTIONS]
@@ -679,6 +680,7 @@ Options:
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn issue_626_panic() {
     static ISSUE_626_PANIC: &str = "\
 Usage: ctest [OPTIONS]
@@ -713,6 +715,7 @@ Options:
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn issue_626_variable_panic() {
     for i in 10..320 {
         let _ = Command::new("ctest")
@@ -731,6 +734,7 @@ fn issue_626_variable_panic() {
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn final_word_wrapping() {
     static FINAL_WORD_WRAPPING: &str = "\
 Usage: ctest
@@ -748,7 +752,10 @@ Options:
     utils::assert_output(cmd, "ctest --help", FINAL_WORD_WRAPPING, false);
 }
 
-static WRAPPING_NEWLINE_CHARS: &str = "\
+#[test]
+#[cfg(feature = "wrap_help")]
+fn wrapping_newline_chars() {
+    static WRAPPING_NEWLINE_CHARS: &str = "\
 Usage: ctest [mode]
 
 Arguments:
@@ -763,8 +770,6 @@ Options:
   -V, --version  Print version information
 ";
 
-#[test]
-fn wrapping_newline_chars() {
     let cmd = Command::new("ctest")
         .version("0.1")
         .term_width(60)
@@ -777,7 +782,23 @@ fn wrapping_newline_chars() {
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn wrapping_newline_variables() {
+    static WRAPPING_NEWLINE_CHARS: &str = "\
+Usage: ctest [mode]
+
+Arguments:
+  [mode]  x, max, maximum   20 characters, contains symbols.
+          l, long           Copy-friendly, 14 characters,
+          contains symbols.
+          m, med, medium    Copy-friendly, 8 characters,
+          contains symbols.
+
+Options:
+  -h, --help     Print help information
+  -V, --version  Print version information
+";
+
     let cmd = Command::new("ctest")
         .version("0.1")
         .term_width(60)
@@ -790,6 +811,7 @@ fn wrapping_newline_variables() {
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn dont_wrap_urls() {
     let cmd = Command::new("Example")
         .term_width(30)
@@ -849,6 +871,7 @@ fn old_newline_variables() {
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn issue_688_hide_pos_vals() {
     static ISSUE_688: &str = "\
 Usage: ctest [OPTIONS]
@@ -1147,6 +1170,7 @@ Options:
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn issue_777_wrap_all_things() {
     static ISSUE_777: &str = "A cmd with a crazy very long long
 long name hahaha 1.0
@@ -1423,6 +1447,7 @@ fn hide_default_val() {
 }
 
 #[test]
+#[cfg(feature = "wrap_help")]
 fn escaped_whitespace_values() {
     static ESCAPED_DEFAULT_VAL: &str = "\
 Usage: default [OPTIONS]