Skip to content

Commit

Permalink
fix(help)!: Merge OPTIONS / FLAGS default groups
Browse files Browse the repository at this point in the history
For those that want the original behavior, you can usxe
`arg.help_heading(Some("FLAGS"))` on your flags.  Limitations:
- This will not give you a special sort order
- This will not get a `[FLAGS]` added to usage

For templates, we removed `{unified}` and `{flags}`.  To help people
catch these, a debug_assert was added.

I'm unsure but I think there might be a change in behavior in calcuating
when to show `[OPTION]` in usage.  The old code only looked at
`required` while flags looked only at arg groups.  We now look at both.

Ideally we'd add these in `_build` and remove special casing for
no-groups except in the sort order of groups.  I feel like thats best
left for later.

This also reduced the scope of `App`s public API.
`get_*_with_no_heading` seemed a bit specialized to be in the public
API.  clap-rs#2853 looks at splitting it out into its own PR.

BREAKING CHANGE: Multiple
- `UnifiedHelpMessage` removed
- `{flags}` and `{unified}` are removed and will assert when present.
- `get_*_with_no_heading` removed

Fixes clap-rs#2807
  • Loading branch information
epage committed Oct 13, 2021
1 parent d833d66 commit cc3c67b
Show file tree
Hide file tree
Showing 33 changed files with 338 additions and 892 deletions.
10 changes: 4 additions & 6 deletions README.md
Expand Up @@ -385,15 +385,13 @@ ARGS:
INPUT The input file to use

USAGE:
MyApp [FLAGS] [OPTIONS] <INPUT> [SUBCOMMAND]

FLAGS:
-h, --help Print help information
-v Sets the level of verbosity
-V, --version Print version information
MyApp [OPTIONS] <INPUT> [SUBCOMMAND]

OPTIONS:
-c, --config <FILE> Sets a custom config file
-h, --help Print help information
-v Sets the level of verbosity
-V, --version Print version information

SUBCOMMANDS:
help Print this message or the help of the given subcommand(s)
Expand Down
2 changes: 1 addition & 1 deletion benches/04_new_help.rs
Expand Up @@ -196,7 +196,7 @@ pub fn example10(c: &mut Criterion) {
}

pub fn example4_template(c: &mut Criterion) {
let mut app = app_example4().help_template("{bin} {version}\n{author}\n{about}\n\nUSAGE:\n {usage}\n\nFLAGS:\n{flags}\n\nARGS:\n{args}\n");
let mut app = app_example4().help_template("{bin} {version}\n{author}\n{about}\n\nUSAGE:\n {usage}\n\nOPTIONS:\n{options}\n\nARGS:\n{args}\n");
c.bench_function("example4_template", |b| b.iter(|| build_help(&mut app)));
}

Expand Down
5 changes: 2 additions & 3 deletions benches/05_ripgrep.rs
Expand Up @@ -3,7 +3,7 @@
//
// CLI used is adapted from ripgrep 48a8a3a691220f9e5b2b08f4051abe8655ea7e8a

use clap::{App, AppSettings, Arg, ArgSettings};
use clap::{App, Arg, ArgSettings};
use criterion::{criterion_group, criterion_main, Criterion};
use std::collections::HashMap;
use std::io::Cursor;
Expand Down Expand Up @@ -267,7 +267,7 @@ ARGS:
{positionals}
OPTIONS:
{unified}";
{options}";

/// Build a clap application with short help strings.
fn app_short() -> App<'static> {
Expand Down Expand Up @@ -306,7 +306,6 @@ where
.version("0.4.0") // Simulating
.about(ABOUT)
.max_term_width(100)
.setting(AppSettings::UnifiedHelpMessage)
.override_usage(USAGE)
.help_template(TEMPLATE)
// Handle help/version manually to make their output formatting
Expand Down
14 changes: 6 additions & 8 deletions clap_derive/README.md
Expand Up @@ -82,22 +82,20 @@ Guillaume Pinot <texitoi@texitoi.eu>, others
A basic example
USAGE:
basic [FLAGS] [OPTIONS] --output <output> [--] [file]...
basic [OPTIONS] --output <output> [--] [file]...
ARGS:
<FILE>... Files to process
FLAGS:
-d, --debug Activate debug mode
-h, --help Print help information
-V, --version Print version information
-v, --verbose Verbose mode (-v, -vv, -vvv, etc.)
OPTIONS:
-l, --level <level>... admin_level to consider
-c, --nb-cars <nb-cars> Number of cars
-d, --debug Activate debug mode
-h, --help Print help information
-l, --level <level>... admin_level to consider
-o, --output <output> Output file
-s, --speed <speed> Set speed [default: 42]
-V, --version Print version information
-v, --verbose Verbose mode (-v, -vv, -vvv, etc.)
ARGS:
<file>... Files to process
Expand Down
2 changes: 1 addition & 1 deletion clap_derive/tests/non_literal_attributes.rs
Expand Up @@ -19,7 +19,7 @@ pub const DISPLAY_ORDER: usize = 2;

// Check if the global settings compile
#[derive(Parser, Debug, PartialEq, Eq)]
#[clap(global_setting = AppSettings::UnifiedHelpMessage)]
#[clap(global_setting = AppSettings::AllowLeadingHyphen)]
struct Opt {
#[clap(
long = "x",
Expand Down
6 changes: 2 additions & 4 deletions site/content/_index.md
Expand Up @@ -50,11 +50,9 @@ Simple program to greet a person
USAGE:
hello [OPTIONS] --name <name>
FLAGS:
-h, --help Print help information
-V, --version Print version information
OPTIONS:
-c, --count <count> Number of times to greet [default: 1]
-h, --help Print help information
-n, --name <name> Name of the person to greet
-V, --version Print version information
```
13 changes: 13 additions & 0 deletions src/build/app/debug_asserts.rs
Expand Up @@ -283,6 +283,19 @@ pub(crate) fn assert_app(app: &App) {
detect_duplicate_flags(&long_flags, "long");
detect_duplicate_flags(&short_flags, "short");

if let Some(help_template) = app.template {
assert!(
!help_template.contains("{flags}"),
"{}",
"`{flags}` template variable was removed in clap3, they are now included in `{options}`"
);
assert!(
!help_template.contains("{unified}"),
"{}",
"`{unified}` template variable was removed in clap3, use `{options}` instead"
);
}

app._panic_on_missing_help(app.g_settings.is_set(AppSettings::HelpRequired));
assert_app_flags(app);
}
Expand Down
49 changes: 23 additions & 26 deletions src/build/app/mod.rs
Expand Up @@ -205,7 +205,7 @@ impl<'help> App<'help> {
self.args.args()
}

/// Iterate through the *positionals*.
/// Iterate through the *positionals* arguments.
#[inline]
pub fn get_positionals(&self) -> impl Iterator<Item = &Arg<'help>> {
self.get_arguments().filter(|a| a.is_positional())
Expand All @@ -223,22 +223,6 @@ impl<'help> App<'help> {
.filter(|a| a.is_set(ArgSettings::TakesValue) && a.get_index().is_none())
}

/// Iterate through the *positionals* that don't have custom heading.
pub fn get_positionals_with_no_heading(&self) -> impl Iterator<Item = &Arg<'help>> {
self.get_positionals()
.filter(|a| a.get_help_heading().is_none())
}

/// Iterate through the *flags* that don't have custom heading.
pub fn get_flags_with_no_heading(&self) -> impl Iterator<Item = &Arg<'help>> {
self.get_flags().filter(|a| a.get_help_heading().is_none())
}

/// Iterate through the *options* that don't have custom heading.
pub fn get_opts_with_no_heading(&self) -> impl Iterator<Item = &Arg<'help>> {
self.get_opts().filter(|a| a.get_help_heading().is_none())
}

// Get a list of subcommands which contain the provided Argument
//
// This command will only include subcommands in its list for which the subcommands
Expand Down Expand Up @@ -862,10 +846,6 @@ impl<'help> App<'help> {
/// * `{usage}` - Automatically generated or given usage string.
/// * `{all-args}` - Help for all arguments (options, flags, positional
/// arguments, and subcommands) including titles.
/// * `{unified}` - Unified help for options and flags. Note, you must *also*
/// set [`AppSettings::UnifiedHelpMessage`] to fully merge both
/// options and flags, otherwise the ordering is "best effort".
/// * `{flags}` - Help for flags.
/// * `{options}` - Help for options.
/// * `{positionals}` - Help for positional arguments.
/// * `{subcommands}` - Help for subcommands.
Expand Down Expand Up @@ -1093,7 +1073,7 @@ impl<'help> App<'help> {
/// header for the specified argument type) until a subsequent call to
/// [`App::help_heading`] or [`App::stop_custom_headings`].
///
/// This is useful if the default `FLAGS`, `OPTIONS`, or `ARGS` headings are
/// This is useful if the default `OPTIONS` or `ARGS` headings are
/// not specific enough for one's use case.
///
/// [`App::arg`]: App::arg()
Expand Down Expand Up @@ -1709,9 +1689,9 @@ impl<'help> App<'help> {
/// cust-ord
///
/// USAGE:
/// cust-ord [FLAGS] [OPTIONS]
/// cust-ord [OPTIONS]
///
/// FLAGS:
/// OPTIONS:
/// -h, --help Print help information
/// -V, --version Print version information
///
Expand Down Expand Up @@ -2177,7 +2157,7 @@ impl<'help> App<'help> {
/// USAGE:
/// myprog [SUBCOMMAND]
///
/// FLAGS:
/// OPTIONS:
/// -h, --help Print help information
/// -V, --version Print version information
///
Expand Down Expand Up @@ -2205,7 +2185,7 @@ impl<'help> App<'help> {
/// USAGE:
/// myprog [THING]
///
/// FLAGS:
/// OPTIONS:
/// -h, --help Print help information
/// -V, --version Print version information
///
Expand Down Expand Up @@ -2651,6 +2631,23 @@ impl<'a, T> Captures<'a> for T {}

// Internal Query Methods
impl<'help> App<'help> {
/// Iterate through the *named* arguments.
pub(crate) fn get_non_positional(&self) -> impl Iterator<Item = &Arg<'help>> {
self.get_arguments().filter(|a| !a.is_positional())
}

/// Iterate through the *positionals* that don't have custom heading.
pub(crate) fn get_positionals_with_no_heading(&self) -> impl Iterator<Item = &Arg<'help>> {
self.get_positionals()
.filter(|a| a.get_help_heading().is_none())
}

/// Iterate through the *named* that don't have custom heading.
pub(crate) fn get_non_positional_with_no_heading(&self) -> impl Iterator<Item = &Arg<'help>> {
self.get_non_positional()
.filter(|a| a.get_help_heading().is_none())
}

pub(crate) fn find(&self, arg_id: &Id) -> Option<&Arg<'help>> {
self.args.args().find(|a| a.id == *arg_id)
}
Expand Down
26 changes: 0 additions & 26 deletions src/build/app/settings.rs
Expand Up @@ -11,7 +11,6 @@ bitflags! {
const ARG_REQUIRED_ELSE_HELP = 1 << 2;
const PROPAGATE_VERSION = 1 << 3;
const DISABLE_VERSION_FOR_SC = 1 << 4;
const UNIFIED_HELP = 1 << 5;
const WAIT_ON_ERROR = 1 << 6;
const SC_REQUIRED_ELSE_HELP = 1 << 7;
const NO_AUTO_HELP = 1 << 8;
Expand Down Expand Up @@ -122,8 +121,6 @@ impl_settings! { AppSettings, AppFlags,
=> Flags::USE_LONG_FORMAT_FOR_HELP_SC,
TrailingVarArg("trailingvararg")
=> Flags::TRAILING_VARARG,
UnifiedHelpMessage("unifiedhelpmessage")
=> Flags::UNIFIED_HELP,
NextLineHelp("nextlinehelp")
=> Flags::NEXT_LINE_HELP,
IgnoreErrors("ignoreerrors")
Expand Down Expand Up @@ -958,25 +955,6 @@ pub enum AppSettings {
/// [`Arg::multiple_values(true)`]: crate::Arg::multiple_values()
TrailingVarArg,

/// Groups flags and options together, presenting a more unified help message
/// (a la `getopts` or `docopt` style).
///
/// The default is that the auto-generated help message will group flags, and options
/// separately.
///
/// **NOTE:** This setting is cosmetic only and does not affect any functionality.
///
/// # Examples
///
/// ```rust
/// # use clap::{App, Arg, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::UnifiedHelpMessage)
/// .get_matches();
/// // running `myprog --help` will display a unified "docopt" or "getopts" style help message
/// ```
UnifiedHelpMessage,

/// Will display a message "Press \[ENTER\]/\[RETURN\] to continue..." and wait for user before
/// exiting
///
Expand Down Expand Up @@ -1164,10 +1142,6 @@ mod test {
"trailingvararg".parse::<AppSettings>().unwrap(),
AppSettings::TrailingVarArg
);
assert_eq!(
"unifiedhelpmessage".parse::<AppSettings>().unwrap(),
AppSettings::UnifiedHelpMessage
);
assert_eq!(
"waitonerror".parse::<AppSettings>().unwrap(),
AppSettings::WaitOnError
Expand Down
8 changes: 4 additions & 4 deletions src/build/app/tests.rs
Expand Up @@ -13,21 +13,21 @@ fn propagate_version() {
#[test]
fn global_setting() {
let mut app = App::new("test")
.global_setting(AppSettings::UnifiedHelpMessage)
.global_setting(AppSettings::AllowLeadingHyphen)
.subcommand(App::new("subcmd"));
app._propagate();
assert!(app
.subcommands
.iter()
.find(|s| s.name == "subcmd")
.unwrap()
.is_set(AppSettings::UnifiedHelpMessage));
.is_set(AppSettings::AllowLeadingHyphen));
}

#[test]
fn global_settings() {
let mut app = App::new("test")
.global_setting(AppSettings::UnifiedHelpMessage)
.global_setting(AppSettings::AllowLeadingHyphen)
.global_setting(AppSettings::TrailingVarArg)
.subcommand(App::new("subcmd"));
app._propagate();
Expand All @@ -36,7 +36,7 @@ fn global_settings() {
.iter()
.find(|s| s.name == "subcmd")
.unwrap()
.is_set(AppSettings::UnifiedHelpMessage));
.is_set(AppSettings::AllowLeadingHyphen));
assert!(app
.subcommands
.iter()
Expand Down

0 comments on commit cc3c67b

Please sign in to comment.