Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help message output is not sorted by option name #4728

Closed
2 tasks done
ghost opened this issue Feb 24, 2023 · 3 comments · Fixed by #4973
Closed
2 tasks done

Help message output is not sorted by option name #4728

ghost opened this issue Feb 24, 2023 · 3 comments · Fixed by #4973
Labels
C-bug Category: Updating dependencies

Comments

@ghost
Copy link

ghost commented Feb 24, 2023

Please complete the following tasks

Rust Version

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

Clap Version

4.1.6

Minimal reproducible code

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

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.

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
@ghost ghost added the C-bug Category: Updating dependencies label Feb 24, 2023
@epage epage closed this as completed in 6e1e754 Feb 24, 2023
@epage
Copy link
Member

epage commented Feb 24, 2023

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.

@ClementTsang
Copy link
Contributor

ClementTsang commented Jun 18, 2023

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:

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.

epage added a commit to epage/clap that referenced this issue Jun 19, 2023
I must have been moving too fast with 6e1e754 as it changed the wrong
part of the text when really the whole thing needed an overhaul.

So this correctly fixed clap-rs#4728
@epage
Copy link
Member

epage commented Jun 19, 2023

I must have been moving too fast with 6e1e754 as it didn't update everything and it was incorrect in the part it did update.

#4973 should now clarify that order is determined by when you add things and that you can override it to force sorting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Updating dependencies
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants