Repository: clap-rs/clap
Pull request #4157 (2022-08-31 15:12:11)

refactor(derive): Be explicit in what is being parsed
<!--
Thanks for helping out!

Please link the appropriate issue from your PR.

If you don't have an issue, we'd recommend starting with one first so the PR can focus on the
implementation (unless its an obvious bug or documentation fix that will have
little conversation).
-->



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d7b1189f..31a9a5fb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,7 +49,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 - *(help)* Subcommands are now listed before arguments.  To get the old behavior, see `Command::help_template`
 - *(help)* Help headings are now title cased, making any user-provided help headings inconsistent.  To get the old behavior, see `Command::help_template`, `Arg::help_heading`, and `Command::subcommand_help_heading`
 - *(help)* "Command" is used as the section heading for subcommands and `COMMAND` for the value name.  To get the old behavior, see  `Command::subcommand_help_heading` and `Arg::subcommand_value_name`
-- *(help)* Whitespace in help output is now trimmed to ensure consistency regardless of how well a template matches the users needs.
 - *(env)* Parse `--help` and `--version` like any `ArgAction::SetTrue` flag (#3776)
 - *(derive)* Changed the default for arguments from `parse` to `value_parser`., removing `parse` support
 - *(derive)* `subcommand_required(true).arg_required_else_help(true)` is set instead of `SubcommandRequiredElseHelp` (#3280)
@@ -100,7 +99,6 @@ Deprecated
 - *(help)* Use `[positional]` in list when relevant (#4144)
 - *(help)* Show all `[positional]` in usage
 - *(help)* Polish up subcommands by referring to them as commands
-- *(help)* Trim extra whitespace to avoid artifacts from different uses of templates
 - *(version)* Use `Command::display_name` rather than `Command::bin_name`
 - *(parser)* Assert on unknown args when using external subcommands (#3703)
 - *(parser)* Always fill in `""` argument for external subcommands (#3263)
diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs
index a77c0c35..d8f69d6b 100644
--- a/clap_derive/src/attrs.rs
+++ b/clap_derive/src/attrs.rs
@@ -54,7 +54,37 @@ pub struct Attrs {
 }
 
 impl Attrs {
-    pub fn from_struct(
+    pub fn from_args_struct(
+        span: Span,
+        attrs: &[Attribute],
+        name: Name,
+        argument_casing: Sp<CasingStyle>,
+        env_casing: Sp<CasingStyle>,
+    ) -> Self {
+        Self::from_struct(span, attrs, name, argument_casing, env_casing)
+    }
+
+    pub fn from_subcommand_enum(
+        span: Span,
+        attrs: &[Attribute],
+        name: Name,
+        argument_casing: Sp<CasingStyle>,
+        env_casing: Sp<CasingStyle>,
+    ) -> Self {
+        Self::from_struct(span, attrs, name, argument_casing, env_casing)
+    }
+
+    pub fn from_value_enum(
+        span: Span,
+        attrs: &[Attribute],
+        name: Name,
+        argument_casing: Sp<CasingStyle>,
+        env_casing: Sp<CasingStyle>,
+    ) -> Self {
+        Self::from_struct(span, attrs, name, argument_casing, env_casing)
+    }
+
+    fn from_struct(
         span: Span,
         attrs: &[Attribute],
         name: Name,
@@ -90,7 +120,7 @@ impl Attrs {
         }
     }
 
-    pub fn from_variant(
+    pub fn from_subcommand_variant(
         variant: &Variant,
         struct_casing: Sp<CasingStyle>,
         env_casing: Sp<CasingStyle>,
@@ -233,7 +263,7 @@ impl Attrs {
         }
     }
 
-    pub fn from_field(
+    pub fn from_args_field(
         field: &Field,
         struct_casing: Sp<CasingStyle>,
         env_casing: Sp<CasingStyle>,
diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs
index 46a26da2..abd8d8f3 100644
--- a/clap_derive/src/derives/args.rs
+++ b/clap_derive/src/derives/args.rs
@@ -57,7 +57,7 @@ pub fn gen_for_struct(
 ) -> TokenStream {
     let from_arg_matches = gen_from_arg_matches_for_struct(struct_name, generics, fields, attrs);
 
-    let attrs = Attrs::from_struct(
+    let attrs = Attrs::from_args_struct(
         Span::call_site(),
         attrs,
         Name::Derived(struct_name.clone()),
@@ -103,7 +103,7 @@ pub fn gen_from_arg_matches_for_struct(
     fields: &Punctuated<Field, Comma>,
     attrs: &[Attribute],
 ) -> TokenStream {
-    let attrs = Attrs::from_struct(
+    let attrs = Attrs::from_args_struct(
         Span::call_site(),
         attrs,
         Name::Derived(struct_name.clone()),
@@ -164,7 +164,7 @@ pub fn gen_augment(
     override_required: bool,
 ) -> TokenStream {
     let mut subcmds = fields.iter().filter_map(|field| {
-        let attrs = Attrs::from_field(
+        let attrs = Attrs::from_args_field(
             field,
             parent_attribute.casing(),
             parent_attribute.env_casing(),
@@ -210,7 +210,7 @@ pub fn gen_augment(
     }
 
     let args = fields.iter().filter_map(|field| {
-        let attrs = Attrs::from_field(
+        let attrs = Attrs::from_args_field(
             field,
             parent_attribute.casing(),
             parent_attribute.env_casing(),
@@ -342,7 +342,7 @@ pub fn gen_augment(
 
 pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_attribute: &Attrs) -> TokenStream {
     let fields = fields.iter().map(|field| {
-        let attrs = Attrs::from_field(
+        let attrs = Attrs::from_args_field(
             field,
             parent_attribute.casing(),
             parent_attribute.env_casing(),
@@ -409,7 +409,7 @@ pub fn gen_updater(
     use_self: bool,
 ) -> TokenStream {
     let fields = fields.iter().map(|field| {
-        let attrs = Attrs::from_field(
+        let attrs = Attrs::from_args_field(
             field,
             parent_attribute.casing(),
             parent_attribute.env_casing(),
diff --git a/clap_derive/src/derives/into_app.rs b/clap_derive/src/derives/into_app.rs
index 7e91e6de..817a1036 100644
--- a/clap_derive/src/derives/into_app.rs
+++ b/clap_derive/src/derives/into_app.rs
@@ -30,7 +30,7 @@ pub fn gen_for_struct(
 ) -> TokenStream {
     let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
 
-    let attrs = Attrs::from_struct(
+    let attrs = Attrs::from_args_struct(
         Span::call_site(),
         attrs,
         Name::Assigned(quote!(#app_name)),
@@ -75,7 +75,7 @@ pub fn gen_for_struct(
 pub fn gen_for_enum(enum_name: &Ident, generics: &Generics, attrs: &[Attribute]) -> TokenStream {
     let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
 
-    let attrs = Attrs::from_struct(
+    let attrs = Attrs::from_subcommand_enum(
         Span::call_site(),
         attrs,
         Name::Assigned(quote!(#app_name)),
diff --git a/clap_derive/src/derives/subcommand.rs b/clap_derive/src/derives/subcommand.rs
index 150860ea..7ce9300f 100644
--- a/clap_derive/src/derives/subcommand.rs
+++ b/clap_derive/src/derives/subcommand.rs
@@ -45,7 +45,7 @@ pub fn gen_for_enum(
 ) -> TokenStream {
     let from_arg_matches = gen_from_arg_matches_for_enum(enum_name, generics, attrs, e);
 
-    let attrs = Attrs::from_struct(
+    let attrs = Attrs::from_subcommand_enum(
         Span::call_site(),
         attrs,
         Name::Derived(enum_name.clone()),
@@ -94,7 +94,7 @@ fn gen_from_arg_matches_for_enum(
     attrs: &[Attribute],
     e: &DataEnum,
 ) -> TokenStream {
-    let attrs = Attrs::from_struct(
+    let attrs = Attrs::from_subcommand_enum(
         Span::call_site(),
         attrs,
         Name::Derived(name.clone()),
@@ -148,7 +148,7 @@ fn gen_augment(
     let subcommands: Vec<_> = variants
         .iter()
         .filter_map(|variant| {
-            let attrs = Attrs::from_variant(
+            let attrs = Attrs::from_subcommand_variant(
                 variant,
                 parent_attribute.casing(),
                 parent_attribute.env_casing(),
@@ -337,7 +337,7 @@ fn gen_has_subcommand(
     let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
         .iter()
         .filter_map(|variant| {
-            let attrs = Attrs::from_variant(
+            let attrs = Attrs::from_subcommand_variant(
                 variant,
                 parent_attribute.casing(),
                 parent_attribute.env_casing(),
@@ -407,7 +407,7 @@ fn gen_from_arg_matches(
     let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
         .iter()
         .filter_map(|variant| {
-            let attrs = Attrs::from_variant(
+            let attrs = Attrs::from_subcommand_variant(
                 variant,
                 parent_attribute.casing(),
                 parent_attribute.env_casing(),
@@ -556,7 +556,7 @@ fn gen_update_from_arg_matches(
     let (flatten, variants): (Vec<_>, Vec<_>) = variants
         .iter()
         .filter_map(|variant| {
-            let attrs = Attrs::from_variant(
+            let attrs = Attrs::from_subcommand_variant(
                 variant,
                 parent_attribute.casing(),
                 parent_attribute.env_casing(),
@@ -582,7 +582,7 @@ fn gen_update_from_arg_matches(
                     .named
                     .iter()
                     .map(|field| {
-                        let attrs = Attrs::from_field(
+                        let attrs = Attrs::from_args_field(
                             field,
                             parent_attribute.casing(),
                             parent_attribute.env_casing(),
diff --git a/clap_derive/src/derives/value_enum.rs b/clap_derive/src/derives/value_enum.rs
index cb66c120..a63bc693 100644
--- a/clap_derive/src/derives/value_enum.rs
+++ b/clap_derive/src/derives/value_enum.rs
@@ -35,7 +35,7 @@ pub fn derive_value_enum(input: &DeriveInput) -> TokenStream {
 }
 
 pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream {
-    let attrs = Attrs::from_struct(
+    let attrs = Attrs::from_value_enum(
         Span::call_site(),
         attrs,
         Name::Derived(name.clone()),
diff --git a/src/output/help.rs b/src/output/help.rs
index b6ff041b..6fe513c8 100644
--- a/src/output/help.rs
+++ b/src/output/help.rs
@@ -106,9 +106,6 @@ impl<'cmd, 'writer> Help<'cmd, 'writer> {
             self.write_templated_help(template);
         }
 
-        // Remove any extra lines caused by book keeping
-        self.writer.trim();
-        // Ensure there is still a trailing newline
         self.none("\n");
     }
 }


Test patch:
diff --git a/tests/builder/help.rs b/tests/builder/help.rs
index 73d2c68e..5ec5e79f 100644
--- a/tests/builder/help.rs
+++ b/tests/builder/help.rs
@@ -1627,7 +1627,7 @@ Options:
 
 NETWORKING:
     -n, --no-proxy    Do not use system proxy settings
-        --port
+        --port        
 ";
 
     let cmd = Command::new("blorp")
@@ -2853,7 +2853,7 @@ fn no_wrap_help() {
     let cmd = Command::new("ctest")
         .term_width(0)
         .override_help(MULTI_SC_HELP);
-    utils::assert_output(cmd, "ctest --help", MULTI_SC_HELP, false);
+    utils::assert_output(cmd, "ctest --help", &format!("{}\n", MULTI_SC_HELP), false);
 }
 
 #[test]