Repository: clap-rs/clap (language: rust)

Issue:
Help message output is not sorted by option name
### Please complete the following tasks

- [X] I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
- [X] I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues

### Rust Version

rustc 1.69.0-nightly (07c993eba 2023-02-23)

### Clap Version

4.1.6

### Minimal reproducible code

```rust
use clap::Parser;

#[derive(Parser)]
struct Args {
    #[clap(short = 'C')]
    c: bool,
    #[clap(short = 'A')]
    a: bool,
    #[clap(short = 'B')]
    b: bool,
}

fn main() {
    _ = Args::parse();
}
```


### Steps to reproduce the bug with the above code

```bash
cargo run -- --help
```

### Actual Behaviour

I get this output:

```
Usage: clap-alpha-test [OPTIONS]

Options:
  -C
  -A
  -B
  -h, --help  Print help
```

That is, the arguments are not in alphabetical order as expected based on the documentation in [`Arg::display_order`](https://docs.rs/clap/latest/clap/builder/struct.Arg.html#method.display_order).

### Expected Behaviour

I would prefer help output to be sorted in alphabetical (or ASCII-betical) order as per previous `clap` versions.

### Additional Context

This appears to be the same bug as #2059.

Experimentation indicates that the help options are sorted first by `display_order` (which I have left at the default of 999 except when exploring this bug) and then by the order they appear in the structs.

I have no strong opinion as to whether they should be sorted by the long option name, short option name, struct field name, or some heuristic based on all of them, but the current behaviour is not great. The workaround of manually sorting the struct fields does not work if one is using `#[clap(flatten)]`.

### Debug Output

```
$ cargo run -- --help
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/clap-alpha-test --help`
[      clap::builder::command] 	Command::_do_parse
[      clap::builder::command] 	Command::_build: name="clap-alpha-test"
[      clap::builder::command] 	Command::_propagate:clap-alpha-test
[      clap::builder::command] 	Command::_check_help_and_version:clap-alpha-test expand_help_tree=false
[      clap::builder::command] 	Command::long_help_exists
[      clap::builder::command] 	Command::_check_help_and_version: Building default --help
[      clap::builder::command] 	Command::_propagate_global_args:clap-alpha-test
[clap::builder::debug_asserts] 	Command::_debug_asserts
[clap::builder::debug_asserts] 	Arg::_debug_asserts:c
[clap::builder::debug_asserts] 	Arg::_debug_asserts:a
[clap::builder::debug_asserts] 	Arg::_debug_asserts:b
[clap::builder::debug_asserts] 	Arg::_debug_asserts:help
[clap::builder::debug_asserts] 	Command::_verify_positionals
[        clap::parser::parser] 	Parser::get_matches_with
[        clap::parser::parser] 	Parser::get_matches_with: Begin parsing 'RawOsStr("--help")' ([45, 45, 104, 101, 108, 112])
[        clap::parser::parser] 	Parser::possible_subcommand: arg=Ok("--help")
[        clap::parser::parser] 	Parser::get_matches_with: sc=None
[        clap::parser::parser] 	Parser::parse_long_arg
[        clap::parser::parser] 	Parser::parse_long_arg: Does it contain '='...
[        clap::parser::parser] 	Parser::parse_long_arg: Found valid arg or flag '--help'
[        clap::parser::parser] 	Parser::parse_long_arg("help"): Presence validated
[        clap::parser::parser] 	Parser::react action=Help, identifier=Some(Long), source=CommandLine
[        clap::parser::parser] 	Help: use_long=true
[      clap::builder::command] 	Command::long_help_exists: false
[      clap::builder::command] 	Command::write_help_err: clap-alpha-test, use_long=false
[      clap::builder::command] 	Command::long_help_exists: false
[          clap::output::help] 	write_help
[ clap::output::help_template] 	HelpTemplate::new cmd=clap-alpha-test, use_long=false
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=c
[ clap::output::help_template] 	HelpTemplate::write_templated_help
[ clap::output::help_template] 	HelpTemplate::write_before_help
[         clap::output::usage] 	Usage::create_usage_no_title
[         clap::output::usage] 	Usage::create_help_usage; incl_reqs=true
[         clap::output::usage] 	Usage::needs_options_tag
[         clap::output::usage] 	Usage::needs_options_tag:iter: f=c
[      clap::builder::command] 	Command::groups_for_arg: id="c"
[         clap::output::usage] 	Usage::needs_options_tag:iter:iter: grp_s="Args"
[         clap::output::usage] 	Usage::needs_options_tag:iter: [OPTIONS] required
[         clap::output::usage] 	Usage::get_args: incls=[]
[         clap::output::usage] 	Usage::get_args: unrolled_reqs=[]
[         clap::output::usage] 	Usage::get_args: ret_val=[]
[         clap::output::usage] 	Usage::create_help_usage: usage=clap-alpha-test [OPTIONS]
[ clap::output::help_template] 	HelpTemplate::write_all_args
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=c
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=a
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=b
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=help
[ clap::output::help_template] 	HelpTemplate::write_args Options
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=c
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=a
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=b
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=help
[ clap::output::help_template] 	HelpTemplate::write_args: arg="help" longest=6
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=c
[ clap::output::help_template] 	HelpTemplate::spec_vals: a=-C
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=a
[ clap::output::help_template] 	HelpTemplate::spec_vals: a=-A
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=b
[ clap::output::help_template] 	HelpTemplate::spec_vals: a=-B
[ clap::output::help_template] 	should_show_arg: use_long=false, arg=help
[ clap::output::help_template] 	HelpTemplate::spec_vals: a=--help
[ clap::output::help_template] 	HelpTemplate::spec_vals: a=-C
[ clap::output::help_template] 	HelpTemplate::short
[ clap::output::help_template] 	HelpTemplate::long
[ clap::output::help_template] 	HelpTemplate::align_to_about: arg=c, next_line_help=false, longest=6
[ clap::output::help_template] 	HelpTemplate::align_to_about: positional=false arg_len=2, spaces=10
[ clap::output::help_template] 	HelpTemplate::help
[ clap::output::help_template] 	HelpTemplate::help: help_width=14, spaces=0, avail=86
[ clap::output::help_template] 	HelpTemplate::spec_vals: a=-A
[ clap::output::help_template] 	HelpTemplate::short
[ clap::output::help_template] 	HelpTemplate::long
[ clap::output::help_template] 	HelpTemplate::align_to_about: arg=a, next_line_help=false, longest=6
[ clap::output::help_template] 	HelpTemplate::align_to_about: positional=false arg_len=2, spaces=10
[ clap::output::help_template] 	HelpTemplate::help
[ clap::output::help_template] 	HelpTemplate::help: help_width=14, spaces=0, avail=86
[ clap::output::help_template] 	HelpTemplate::spec_vals: a=-B
[ clap::output::help_template] 	HelpTemplate::short
[ clap::output::help_template] 	HelpTemplate::long
[ clap::output::help_template] 	HelpTemplate::align_to_about: arg=b, next_line_help=false, longest=6
[ clap::output::help_template] 	HelpTemplate::align_to_about: positional=false arg_len=2, spaces=10
[ clap::output::help_template] 	HelpTemplate::help
[ clap::output::help_template] 	HelpTemplate::help: help_width=14, spaces=0, avail=86
[ clap::output::help_template] 	HelpTemplate::spec_vals: a=--help
[ clap::output::help_template] 	HelpTemplate::short
[ clap::output::help_template] 	HelpTemplate::long
[ clap::output::help_template] 	HelpTemplate::align_to_about: arg=help, next_line_help=false, longest=6
[ clap::output::help_template] 	HelpTemplate::align_to_about: positional=false arg_len=6, spaces=2
[ clap::output::help_template] 	HelpTemplate::help
[ clap::output::help_template] 	HelpTemplate::help: help_width=14, spaces=10, avail=86
[ clap::output::help_template] 	HelpTemplate::write_after_help
[      clap::builder::command] 	Command::color: Color setting...
[      clap::builder::command] 	Auto
[      clap::builder::command] 	Command::color: Color setting...
[      clap::builder::command] 	Auto
Usage: clap-alpha-test [OPTIONS]

Options:
  -C
  -A
  -B
  -h, --help  Print help
```


Discussion / hints:
This was stale documentation.  In clap v3 we sorted arguments and commands but in v4 we switched exclusively to what we had called the derive display order.  This provides a more WYSIWYG experience in writing help and was a very popular setting.  As users can get a sorted order manually (or by writing code to manually sort), we felt that keeping this configurable did  not carry enough weight as we looked to simplify the API.
Hi, does this also affect the builder-pattern style? I just noticed that an application I recently updated to clap v4 also has the same new behaviour, [despite the latest documentation claiming](https://docs.rs/clap/latest/clap/struct.Arg.html#method.display_order):

```rust
let m = Command::new("prog")
    .arg(Arg::new("a") // Typically args are grouped alphabetically by name.
                             // Args without a display_order have a value of 999 and are
                             // displayed alphabetically with all other 999 valued args.
```


Fix patch:
diff --git a/clap_builder/src/builder/arg.rs b/clap_builder/src/builder/arg.rs
index 53c45830a5d..65d7cc956e7 100644
--- a/clap_builder/src/builder/arg.rs
+++ b/clap_builder/src/builder/arg.rs
@@ -2175,12 +2175,15 @@ impl Arg {
 
     /// Allows custom ordering of args within the help message.
     ///
-    /// Args with a lower value will be displayed first in the help message. This is helpful when
-    /// one would like to emphasise frequently used args, or prioritize those towards the top of
-    /// the list. Args with duplicate display orders will be displayed in the order they are
-    /// defined.
+    /// `Arg`s with a lower value will be displayed first in the help message.
+    /// Those with the same display order will be sorted.
     ///
-    /// **NOTE:** The default is 999 for all arguments.
+    /// `Arg`s are automatically assigned a display order based on the order they are added to the
+    /// [`Command`][crate::Command].
+    /// Overriding this is helpful when the order arguments are added in isn't the same as the
+    /// display order, whether in one-off cases or to automatically sort arguments.
+    ///
+    /// To change, see [`Command::next_display_order`][crate::Command::next_display_order].
     ///
     /// **NOTE:** This setting is ignored for [positional arguments] which are always displayed in
     /// [index] order.
@@ -2192,22 +2195,23 @@ impl Arg {
     /// # use clap_builder as clap;
     /// # use clap::{Command, Arg, ArgAction};
     /// let m = Command::new("prog")
-    ///     .arg(Arg::new("a") // Typically args are grouped alphabetically by name.
-    ///                              // Args without a display_order have a value of 999 and are
-    ///                              // displayed alphabetically with all other 999 valued args.
-    ///         .long("long-option")
-    ///         .short('o')
+    ///     .arg(Arg::new("boat")
+    ///         .short('b')
+    ///         .long("boat")
     ///         .action(ArgAction::Set)
+    ///         .display_order(0)  // Sort
     ///         .help("Some help and text"))
-    ///     .arg(Arg::new("b")
-    ///         .long("other-option")
-    ///         .short('O')
+    ///     .arg(Arg::new("airplane")
+    ///         .short('a')
+    ///         .long("airplane")
     ///         .action(ArgAction::Set)
-    ///         .display_order(1)   // In order to force this arg to appear *first*
-    ///                             // all we have to do is give it a value lower than 999.
-    ///                             // Any other args with a value of 1 will be displayed
-    ///                             // alphabetically with this one...then 2 values, then 3, etc.
+    ///         .display_order(0)  // Sort
     ///         .help("I should be first!"))
+    ///     .arg(Arg::new("custom-help")
+    ///         .short('?')
+    ///         .action(ArgAction::Help)
+    ///         .display_order(100)  // Don't sort
+    ///         .help("Alt help"))
     ///     .get_matches_from(vec![
     ///         "prog", "--help"
     ///     ]);
@@ -2222,10 +2226,10 @@ impl Arg {
     /// Usage: cust-ord [OPTIONS]
     ///
     /// Options:
-    ///     -h, --help                Print help information
-    ///     -V, --version             Print version information
-    ///     -O, --other-option <b>    I should be first!
-    ///     -o, --long-option <a>     Some help and text
+    ///     -a, --airplane <airplane>    I should be first!
+    ///     -b, --boat <boar>            Some help and text
+    ///     -h, --help                   Print help information
+    ///     -?                           Alt help
     /// ```
     /// [positional arguments]: Arg::index()
     /// [index]: Arg::index()
diff --git a/clap_builder/src/builder/command.rs b/clap_builder/src/builder/command.rs
index 0363f4b8f0f..5756e7dfd84 100644
--- a/clap_builder/src/builder/command.rs
+++ b/clap_builder/src/builder/command.rs
@@ -2598,13 +2598,13 @@ impl Command {
 
     /// Set the placement of this subcommand within the help.
     ///
-    /// Subcommands with a lower value will be displayed first in the help message.  Subcommands
-    /// with duplicate display orders will be displayed in order they are defined.
+    /// Subcommands with a lower value will be displayed first in the help message.
+    /// Those with the same display order will be sorted.
     ///
-    /// This is helpful when one would like to emphasize frequently used subcommands, or prioritize
-    /// those towards the top of the list.
-    ///
-    /// **NOTE:** The default is 999 for all subcommands.
+    /// `Command`s are automatically assigned a display order based on the order they are added to
+    /// their parent [`Command`].
+    /// Overriding this is helpful when the order commands are added in isn't the same as the
+    /// display order, whether in one-off cases or to automatically sort commands.
     ///
     /// # Examples
     ///
@@ -2613,17 +2613,11 @@ impl Command {
     /// # use clap_builder as clap;
     /// # use clap::{Command, };
     /// let m = Command::new("cust-ord")
-    ///     .subcommand(Command::new("alpha") // typically subcommands are grouped
-    ///                                                // alphabetically by name. Subcommands
-    ///                                                // without a display_order have a value of
-    ///                                                // 999 and are displayed alphabetically with
-    ///                                                // all other 999 subcommands
-    ///         .about("Some help and text"))
     ///     .subcommand(Command::new("beta")
-    ///         .display_order(1)   // In order to force this subcommand to appear *first*
-    ///                             // all we have to do is give it a value lower than 999.
-    ///                             // Any other subcommands with a value of 1 will be displayed
-    ///                             // alphabetically with this one...then 2 values, then 3, etc.
+    ///         .display_order(0)  // Sort
+    ///         .about("Some help and text"))
+    ///     .subcommand(Command::new("alpha")
+    ///         .display_order(0)  // Sort
     ///         .about("I should be first!"))
     ///     .get_matches_from(vec![
     ///         "cust-ord", "--help"
@@ -2639,8 +2633,9 @@ impl Command {
     /// Usage: cust-ord [OPTIONS]
     ///
     /// Commands:
-    ///     beta    I should be first!
-    ///     alpha   Some help and text
+    ///     alpha    I should be first!
+    ///     beta     Some help and text
+    ///     help     Print help for the subcommand(s)
     ///
     /// Options:
     ///     -h, --help       Print help


Test patch: