Repository: kbknapp/clap-rs
Pull request #1208 (2018-03-12 00:38:31)

Print ARGS after usage in help
For version 3, we want the args section to immediately follow
the usage section in the default help message.

One change that I am unhappy with is needing to make "write_arg"
in app/help.rs accept an extra param that makes it suppress the
extra line. This is to prevent an extra blank line from appearing
between args and options in the default help, and seems necessary,
but there might be a better way.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/kbknapp/clap-rs/1208)
<!-- Reviewable:end -->



Patch:
diff --git a/src/app/help.rs b/src/app/help.rs
index 4ec25197..be588891 100644
--- a/src/app/help.rs
+++ b/src/app/help.rs
@@ -191,13 +191,16 @@ impl<'w> Help<'w> {
             arg_v.push(arg)
         }
         let mut first = true;
+        let arg_c = arg_v.len();
+        let mut current_arg_ix = 0;
         for arg in arg_v {
             if first {
                 first = false;
             } else {
                 self.writer.write_all(b"\n")?;
             }
-            self.write_arg(arg)?;
+            self.write_arg(arg, current_arg_ix < arg_c)?;
+            current_arg_ix += 1;
         }
         Ok(())
     }
@@ -235,19 +238,19 @@ impl<'w> Help<'w> {
                 } else {
                     self.writer.write_all(b"\n")?;
                 }
-                self.write_arg(arg)?;
+                self.write_arg(arg, false)?;
             }
         }
         Ok(())
     }
 
     /// Writes help for an argument to the wrapped stream.
-    fn write_arg<'b, 'c>(&mut self, arg: &Arg<'b, 'c>) -> io::Result<()> {
+    fn write_arg<'b, 'c>(&mut self, arg: &Arg<'b, 'c>, prevent_nlh: bool) -> io::Result<()> {
         debugln!("Help::write_arg;");
         self.short(arg)?;
         self.long(arg)?;
         let spec_vals = self.val(arg)?;
-        self.help(arg, &*spec_vals)?;
+        self.help(arg, &*spec_vals, prevent_nlh)?;
         Ok(())
     }
 
@@ -423,7 +426,7 @@ impl<'w> Help<'w> {
     }
 
     /// Writes argument's help to the wrapped stream.
-    fn help<'b, 'c>(&mut self, arg: &Arg<'b, 'c>, spec_vals: &str) -> io::Result<()> {
+    fn help<'b, 'c>(&mut self, arg: &Arg<'b, 'c>, spec_vals: &str, prevent_nlh: bool) -> io::Result<()> {
         debugln!("Help::help;");
         let h = if self.use_long {
             arg.long_help.unwrap_or_else(|| arg.help.unwrap_or(""))
@@ -473,7 +476,7 @@ impl<'w> Help<'w> {
             }
             write!(self.writer, "{}", part)?;
         }
-        if !help.contains('\n') && (nlh || self.force_next_line) {
+        if !prevent_nlh && !help.contains('\n') && (nlh || self.force_next_line) {
             write!(self.writer, "\n")?;
         }
         Ok(())
@@ -688,17 +691,32 @@ impl<'w> Help<'w> {
         let opts = parser.has_opts();
         let subcmds = parser.has_visible_subcommands();
 
-        let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage);
-
         let mut first = true;
 
+        if pos {
+            if !first {
+                self.writer.write_all(b"\n\n")?;
+            }
+            color!(self, "ARGS:\n", warning)?;
+            self.write_args_unsorted(positionals!(parser.app))?;
+            first = false;
+        }
+
+        let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage);
+
         if unified_help && (flags || opts) {
             let opts_flags = args!(parser.app).filter(|a| a.has_switch());
+            if !first {
+                self.writer.write_all(b"\n\n")?;
+            }
             color!(self, "OPTIONS:\n", warning)?;
             self.write_args(opts_flags)?;
             first = false;
         } else {
             if flags {
+                if !first {
+                    self.writer.write_all(b"\n\n")?;
+                }
                 color!(self, "FLAGS:\n", warning)?;
                 self.write_args(flags!(parser.app))?;
                 first = false;
@@ -713,15 +731,6 @@ impl<'w> Help<'w> {
             }
         }
 
-        if pos {
-            if !first {
-                self.writer.write_all(b"\n\n")?;
-            }
-            color!(self, "ARGS:\n", warning)?;
-            self.write_args_unsorted(positionals!(parser.app))?;
-            first = false;
-        }
-
         if subcmds {
             if !first {
                 self.writer.write_all(b"\n\n")?;


Test patch:
diff --git a/tests/app_settings.rs b/tests/app_settings.rs
index d2dad0aa..488abf1f 100644
--- a/tests/app_settings.rs
+++ b/tests/app_settings.rs
@@ -19,14 +19,14 @@ static DONT_COLLAPSE_ARGS: &'static str = "clap-test v1.4.8
 USAGE:
     clap-test [arg1] [arg2] [arg3]
 
-FLAGS:
-    -h, --help       Prints help information
-    -V, --version    Prints version information
-
 ARGS:
     <arg1>    some
     <arg2>    some
-    <arg3>    some";
+    <arg3>    some
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information";
 
 static REQUIRE_EQUALS: &'static str = "clap-test v1.4.8
 
@@ -47,14 +47,14 @@ tests stuff
 USAGE:
     test [OPTIONS] [arg1]
 
+ARGS:
+    <arg1>    some pos arg
+
 OPTIONS:
     -f, --flag            some flag
     -h, --help            Prints help information
         --option <opt>    some option
-    -V, --version         Prints version information
-
-ARGS:
-    <arg1>    some pos arg";
+    -V, --version         Prints version information";
 
 static SKIP_POS_VALS: &'static str = "test 1.3
 Kevin K.
@@ -63,15 +63,15 @@ tests stuff
 USAGE:
     test [OPTIONS] [arg1]
 
+ARGS:
+    <arg1>    some pos arg
+
 FLAGS:
     -h, --help       Prints help information
     -V, --version    Prints version information
 
 OPTIONS:
-    -o, --opt <opt>    some option
-
-ARGS:
-    <arg1>    some pos arg";
+    -o, --opt <opt>    some option";
 
 #[test]
 fn sub_command_negate_required() {
diff --git a/tests/help.rs b/tests/help.rs
index af58bb35..7b72b88d 100644
--- a/tests/help.rs
+++ b/tests/help.rs
@@ -27,6 +27,11 @@ tests clap library
 USAGE:
     clap-test [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]
 
+ARGS:
+    <positional>        tests positionals
+    <positional2>       tests positionals with exclusions
+    <positional3>...    tests specific values [possible values: vi, emacs]
+
 FLAGS:
     -f, --flag       tests flags
     -F               tests flags with exclusions
@@ -42,11 +47,6 @@ OPTIONS:
         --multvalsmo <one> <two>     Tests mutliple values, and mult occs
     -o, --option <opt>...            tests options
 
-ARGS:
-    <positional>        tests positionals
-    <positional2>       tests positionals with exclusions
-    <positional3>...    tests specific values [possible values: vi, emacs]
-
 SUBCOMMANDS:
     help      Prints this message or the help of the given subcommand(s)
     subcmd    tests subcommands";
@@ -57,6 +57,9 @@ USAGE:
     prog --opt <FILE> [PATH]
     prog [PATH] <SUBCOMMAND>
 
+ARGS:
+    <PATH>    help
+
 FLAGS:
     -h, --help       Prints help information
     -V, --version    Prints version information
@@ -64,9 +67,6 @@ FLAGS:
 OPTIONS:
     -o, --opt <FILE>    tests options
 
-ARGS:
-    <PATH>    help
-
 SUBCOMMANDS:
     help    Prints this message or the help of the given subcommand(s)
     test";
@@ -77,6 +77,9 @@ USAGE:
     prog [FLAGS] [OPTIONS] [PATH]
     prog <SUBCOMMAND>
 
+ARGS:
+    <PATH>    help
+
 FLAGS:
     -f, --flag       testing flags
     -h, --help       Prints help information
@@ -85,9 +88,6 @@ FLAGS:
 OPTIONS:
     -o, --opt <FILE>    tests options
 
-ARGS:
-    <PATH>    help
-
 SUBCOMMANDS:
     help    Prints this message or the help of the given subcommand(s)
     test";
@@ -126,6 +126,9 @@ tests subcommands
 USAGE:
     clap-test subcmd [FLAGS] [OPTIONS] [--] [scpositional]
 
+ARGS:
+    <scpositional>    tests positionals
+
 FLAGS:
     -f, --flag       tests flags
     -h, --help       Prints help information
@@ -133,26 +136,23 @@ FLAGS:
 
 OPTIONS:
     -o, --option <scoption>...     tests options
-    -s, --subcmdarg <subcmdarg>    tests other args
-
-ARGS:
-    <scpositional>    tests positionals";
+    -s, --subcmdarg <subcmdarg>    tests other args";
 
 static ISSUE_1046_HIDDEN_SCS: &'static str = "prog 1.0
 
 USAGE:
     prog [FLAGS] [OPTIONS] [PATH]
 
+ARGS:
+    <PATH>    some
+
 FLAGS:
     -f, --flag       testing flags
     -h, --help       Prints help information
     -V, --version    Prints version information
 
 OPTIONS:
-    -o, --opt <FILE>    tests options
-
-ARGS:
-    <PATH>    some";
+    -o, --opt <FILE>    tests options";
 
 // Using number_of_values(1) with multiple(true) misaligns help message
 static ISSUE_760: &'static str = "ctest 0.1
@@ -276,17 +276,17 @@ static WRAPPING_NEWLINE_CHARS: &'static str = "ctest 0.1
 USAGE:
     ctest [mode]
 
-FLAGS:
-    -h, --help       Prints help information
-    -V, --version    Prints version information
-
 ARGS:
     <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.";
+              characters, contains symbols.
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information";
 
 static ISSUE_688: &'static str = "ctest 0.1
 
@@ -309,6 +309,10 @@ bar
 USAGE:
     myapp [OPTIONS] [--] [ARGS]
 
+ARGS:
+    <arg1>       some option
+    <arg2>...    some option
+
 FLAGS:
     -h, --help       Prints help information
     -V, --version    Prints version information
@@ -316,11 +320,7 @@ FLAGS:
 OPTIONS:
     -l, --label <label>...    a label
     -o, --other <other>       some other option
-    -s, --some <some>         some option
-
-ARGS:
-    <arg1>       some option
-    <arg2>...    some option";
+    -s, --some <some>         some option";
 
 static ISSUE_777: &'static str = "A app with a crazy very long long
 long name hahaha 1.0
@@ -356,14 +356,14 @@ static LAST_ARG: &'static str = "last 0.1
 USAGE:
     last <TARGET> [CORPUS] [-- <ARGS>...]
 
-FLAGS:
-    -h, --help       Prints help information
-    -V, --version    Prints version information
-
 ARGS:
     <TARGET>     some
     <CORPUS>     some
-    <ARGS>...    some";
+    <ARGS>...    some
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information";
 
 static LAST_ARG_SC: &'static str = "last 0.1
 
@@ -371,15 +371,15 @@ USAGE:
     last <TARGET> [CORPUS] [-- <ARGS>...]
     last <SUBCOMMAND>
 
-FLAGS:
-    -h, --help       Prints help information
-    -V, --version    Prints version information
-
 ARGS:
     <TARGET>     some
     <CORPUS>     some
     <ARGS>...    some
 
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information
+
 SUBCOMMANDS:
     help    Prints this message or the help of the given subcommand(s)
     test    some";
@@ -389,14 +389,14 @@ static LAST_ARG_REQ: &'static str = "last 0.1
 USAGE:
     last <TARGET> [CORPUS] -- <ARGS>...
 
-FLAGS:
-    -h, --help       Prints help information
-    -V, --version    Prints version information
-
 ARGS:
     <TARGET>     some
     <CORPUS>     some
-    <ARGS>...    some";
+    <ARGS>...    some
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information";
 
 static LAST_ARG_REQ_SC: &'static str = "last 0.1
 
@@ -404,15 +404,15 @@ USAGE:
     last <TARGET> [CORPUS] -- <ARGS>...
     last <SUBCOMMAND>
 
-FLAGS:
-    -h, --help       Prints help information
-    -V, --version    Prints version information
-
 ARGS:
     <TARGET>     some
     <CORPUS>     some
     <ARGS>...    some
 
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information
+
 SUBCOMMANDS:
     help    Prints this message or the help of the given subcommand(s)
     test    some";
@@ -434,6 +434,10 @@ static LAST_ARG_USAGE: &'static str = "flamegraph 0.1
 USAGE:
     flamegraph [FLAGS] [OPTIONS] [BINFILE] [-- <ARGS>...]
 
+ARGS:
+    <BINFILE>    The path of the binary to be profiled. for a binary.
+    <ARGS>...    Any arguments you wish to pass to the being profiled.
+
 FLAGS:
     -h, --help       Prints help information
     -V, --version    Prints version information
@@ -441,24 +445,20 @@ FLAGS:
 
 OPTIONS:
     -f, --frequency <HERTZ>    The sampling frequency.
-    -t, --timeout <SECONDS>    Timeout in seconds.
-
-ARGS:
-    <BINFILE>    The path of the binary to be profiled. for a binary.
-    <ARGS>...    Any arguments you wish to pass to the being profiled.";
+    -t, --timeout <SECONDS>    Timeout in seconds.";
 
 static LAST_ARG_REQ_MULT: &'static str = "example 1.0
 
 USAGE:
     example <FIRST>... [--] <SECOND>...
 
-FLAGS:
-    -h, --help       Prints help information
-    -V, --version    Prints version information
-
 ARGS:
     <FIRST>...     First
-    <SECOND>...    Second";
+    <SECOND>...    Second
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information";
 
 static DEFAULT_HELP: &'static str = "ctest 1.0
 
@@ -478,17 +478,16 @@ that should be displayed
 USAGE:
     myapp [arg1]
 
+ARGS:
+    <arg1>    
+            some option
+
 FLAGS:
     -h, --help       
             Prints help information
 
     -V, --version    
-            Prints version information
-
-
-ARGS:
-    <arg1>    
-            some option";
+            Prints version information";
 
 static HIDE_ENV_VALS: &'static str = "ctest 0.1
 
diff --git a/tests/template_help.rs b/tests/template_help.rs
index adcf495d..ca209b32 100644
--- a/tests/template_help.rs
+++ b/tests/template_help.rs
@@ -34,6 +34,9 @@ Does awesome things
 USAGE:
     MyApp [FLAGS] [OPTIONS] <output> [SUBCOMMAND]
 
+ARGS:
+    <output>    Sets an optional output file
+
 FLAGS:
     -d               Turn debugging information on
     -h, --help       Prints help information
@@ -42,9 +45,6 @@ FLAGS:
 OPTIONS:
     -c, --config <FILE>    Sets a custom config file
 
-ARGS:
-    <output>    Sets an optional output file
-
 SUBCOMMANDS:
     help    Prints this message or the help of the given subcommand(s)
     test    does testing things";