diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 406b528079c..f7ee2427fa7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -167,7 +167,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
- toolchain: "1.90" # STABLE
+ toolchain: "1.91" # STABLE
- uses: Swatinem/rust-cache@v2
- name: UI Tests
run: make test-ui-${{ matrix.features }}
@@ -210,7 +210,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
- toolchain: "1.90" # STABLE
+ toolchain: "1.91" # STABLE
- uses: Swatinem/rust-cache@v2
- name: Check documentation
env:
@@ -225,7 +225,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
- toolchain: "1.90" # STABLE
+ toolchain: "1.91" # STABLE
components: rustfmt
- uses: Swatinem/rust-cache@v2
- name: Check formatting
@@ -239,7 +239,7 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
- toolchain: "1.90" # STABLE
+ toolchain: "1.91" # STABLE
components: clippy
- uses: Swatinem/rust-cache@v2
- name: Lint (ultra-minimal)
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index 1c06b6e58c0..c203003e970 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- - uses: actions/setup-python@v5
+ - uses: actions/setup-python@v6
with:
python-version: '3.x'
- uses: pre-commit/action@v3.0.1
diff --git a/Cargo.lock b/Cargo.lock
index 9c48a66c2f7..e0871b158dc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -498,7 +498,7 @@ dependencies = [
[[package]]
name = "clap_complete"
-version = "4.5.59"
+version = "4.5.61"
dependencies = [
"automod",
"clap 4.5.51",
@@ -513,7 +513,7 @@ dependencies = [
[[package]]
name = "clap_complete_nushell"
-version = "4.5.9"
+version = "4.5.10"
dependencies = [
"clap 4.5.51",
"clap_complete",
diff --git a/Makefile b/Makefile
index 56e525477a3..c3560a40f1b 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ ifneq (${TOOLCHAIN_TARGET},)
ARGS+=--target ${TOOLCHAIN_TARGET}
endif
-STABLE?=1.90
+STABLE?=1.91
_FEATURES = minimal default wasm full debug release
_FEATURES_minimal = --no-default-features --features "std"
diff --git a/clap_builder/src/builder/arg.rs b/clap_builder/src/builder/arg.rs
index edba6a48eb0..7df14fcc337 100644
--- a/clap_builder/src/builder/arg.rs
+++ b/clap_builder/src/builder/arg.rs
@@ -492,7 +492,7 @@ impl Arg {
///
///
///
- /// **NOTE:** When utilized with [`Arg::num_args(1..)`], only the **last** positional argument
+ /// **NOTE:** When utilized with [`Arg::num_args(1..)`][Arg::num_args], only the **last** positional argument
/// may be defined as having a variable number of arguments (i.e. with the highest index)
///
///
@@ -1359,7 +1359,7 @@ impl Arg {
///
///
///
- /// **NOTE:** implicitly sets [`Arg::action(ArgAction::Set)`].
+ /// **NOTE:** implicitly sets [`Arg::action(ArgAction::Set)`][ArgAction::Set].
///
///
///
@@ -1487,7 +1487,7 @@ impl Arg {
///
/// **WARNING:** Prior arguments with `allow_hyphen_values(true)` get precedence over known
/// flags but known flags get precedence over the next possible positional argument with
- /// `allow_hyphen_values(true)`. When combined with [`Arg::num_args(..)`],
+ /// `allow_hyphen_values(true)`. When combined with [`Arg::num_args(..)`][Arg::num_args],
/// [`Arg::value_terminator`] is one way to ensure processing stops.
///
///
diff --git a/clap_builder/src/builder/command.rs b/clap_builder/src/builder/command.rs
index 70861ee491a..c65c67a02de 100644
--- a/clap_builder/src/builder/command.rs
+++ b/clap_builder/src/builder/command.rs
@@ -27,6 +27,7 @@ use crate::output::fmt::Stream;
use crate::output::{fmt::Colorizer, write_help, Usage};
use crate::parser::{ArgMatcher, ArgMatches, Parser};
use crate::util::ChildGraph;
+use crate::util::FlatSet;
use crate::util::{color::ColorChoice, Id};
use crate::{Error, INTERNAL_ERROR_MSG};
@@ -104,6 +105,7 @@ pub struct Command {
current_disp_ord: Option,
subcommand_value_name: Option,
subcommand_heading: Option,
+ help_heading: Option>,
external_value_parser: Option,
long_help_exists: bool,
deferred: Option Command>,
@@ -3701,6 +3703,82 @@ impl Command {
self.subcommand_heading = heading.into_resettable().into_option();
self
}
+
+ /// Set a custom help heading
+ ///
+ /// To place the `help` subcommand under a custom heading amend the default
+ /// heading using [`Command::subcommand_help_heading`].
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg};
+ /// Command::new("myprog")
+ /// .version("2.6")
+ /// .subcommand(
+ /// Command::new("show")
+ /// .about("Help for show")
+ /// )
+ /// .print_help()
+ /// # ;
+ /// ```
+ ///
+ /// will produce
+ ///
+ /// ```text
+ /// myprog
+ ///
+ /// Usage: myprog [COMMAND]
+ ///
+ /// Commands:
+ /// help Print this message or the help of the given subcommand(s)
+ /// show Help for show
+ ///
+ /// Options:
+ /// -h, --help Print help
+ /// -V, --version Print version
+ /// ```
+ ///
+ /// but usage of `help_heading`
+ ///
+ /// ```rust
+ /// # use clap_builder as clap;
+ /// # use clap::{Command, Arg};
+ /// Command::new("myprog")
+ /// .version("2.6")
+ /// .subcommand(
+ /// Command::new("show")
+ /// .about("Help for show")
+ /// .help_heading("Custom heading"),
+ /// )
+ /// .print_help()
+ /// # ;
+ /// ```
+ ///
+ /// will produce
+ ///
+ /// ```text
+ /// myprog
+ ///
+ /// Usage: myprog [COMMAND]
+ ///
+ /// Commands:
+ /// help Print this message or the help of the given subcommand(s)
+ ///
+ /// Custom heading:
+ /// show Help for show
+ ///
+ /// Options:
+ /// -h, --help Print help
+ /// -V, --version Print version
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn help_heading(mut self, heading: impl IntoResettable) -> Self {
+ self.help_heading = Some(heading.into_resettable().into_option());
+ self
+ }
}
/// # Reflection
@@ -3940,6 +4018,15 @@ impl Command {
self.subcommand_heading.as_deref()
}
+ /// Get the help heading specified for this command, if any
+ #[inline]
+ pub fn get_help_heading(&self) -> Option<&str> {
+ self.help_heading
+ .as_ref()
+ .map(|s| s.as_deref())
+ .unwrap_or_default()
+ }
+
/// Returns the subcommand value name.
#[inline]
pub fn get_subcommand_value_name(&self) -> Option<&str> {
@@ -4341,6 +4428,13 @@ impl Command {
}
}
+ #[allow(dead_code)] // overcome clippy minimal report that it is unused.
+ pub(crate) fn get_subcommand_custom_help_headings(&self) -> FlatSet<&str> {
+ self.get_subcommands()
+ .filter_map(|sc| sc.get_help_heading())
+ .collect::>()
+ }
+
fn _do_parse(
&mut self,
raw_args: &mut clap_lex::RawArgs,
@@ -5218,6 +5312,7 @@ impl Default for Command {
current_disp_ord: Some(0),
subcommand_value_name: Default::default(),
subcommand_heading: Default::default(),
+ help_heading: Default::default(),
external_value_parser: Default::default(),
long_help_exists: false,
deferred: None,
diff --git a/clap_builder/src/error/kind.rs b/clap_builder/src/error/kind.rs
index 298a4fd3277..7c487350eb6 100644
--- a/clap_builder/src/error/kind.rs
+++ b/clap_builder/src/error/kind.rs
@@ -224,7 +224,7 @@ pub enum ErrorKind {
/// Occurs when the user provides a value containing invalid UTF-8.
///
/// To allow arbitrary data
- /// - Set [`Arg::value_parser(value_parser!(OsString))`] for argument values
+ /// - Set [`Arg::value_parser(value_parser!(OsString))`][crate::Arg::value_parser] for argument values
/// - Set [`Command::external_subcommand_value_parser`] for external-subcommand
/// values
///
diff --git a/clap_builder/src/output/help_template.rs b/clap_builder/src/output/help_template.rs
index a0bc70fee1e..5542f6231b9 100644
--- a/clap_builder/src/output/help_template.rs
+++ b/clap_builder/src/output/help_template.rs
@@ -389,7 +389,7 @@ impl HelpTemplate<'_, '_> {
.collect::>();
let subcmds = self.cmd.has_visible_subcommands();
- let custom_headings = self
+ let custom_arg_headings = self
.cmd
.get_arguments()
.filter_map(|arg| arg.get_help_heading())
@@ -434,8 +434,8 @@ impl HelpTemplate<'_, '_> {
let _ = write!(self.writer, "{header}{help_heading}:{header:#}\n",);
self.write_args(&non_pos, "Options", option_sort_key);
}
- if !custom_headings.is_empty() {
- for heading in custom_headings {
+ if !custom_arg_headings.is_empty() {
+ for heading in custom_arg_headings {
let args = self
.cmd
.get_arguments()
@@ -879,8 +879,6 @@ impl HelpTemplate<'_, '_> {
cmd.get_name(),
*first
);
- use std::fmt::Write as _;
- let header = &self.styles.get_header();
let mut ord_v = BTreeMap::new();
for subcommand in cmd
@@ -892,44 +890,95 @@ impl HelpTemplate<'_, '_> {
subcommand,
);
}
- for (_, subcommand) in ord_v {
- if !*first {
- self.writer.push_str("\n\n");
- }
- *first = false;
- let heading = subcommand.get_usage_name_fallback();
- let about = subcommand
- .get_about()
- .or_else(|| subcommand.get_long_about())
- .unwrap_or_default();
+ let custom_sc_headings = self.cmd.get_subcommand_custom_help_headings();
- let _ = write!(self.writer, "{header}{heading}:{header:#}",);
- if !about.is_empty() {
- let _ = write!(self.writer, "\n{about}",);
- }
+ // Commands under default heading
+ self.write_flat_subcommands_under_heading(&ord_v, None, first, false);
- let args = subcommand
- .get_arguments()
- .filter(|arg| should_show_arg(self.use_long, arg) && !arg.is_global_set())
- .collect::>();
- if !args.is_empty() {
- self.writer.push_str("\n");
- }
+ // Commands under custom headings
+ for heading in custom_sc_headings {
+ self.write_flat_subcommands_under_heading(&ord_v, Some(heading), first, false);
+ }
- let mut sub_help = HelpTemplate {
- writer: self.writer,
- cmd: subcommand,
- styles: self.styles,
- usage: self.usage,
- next_line_help: self.next_line_help,
- term_w: self.term_w,
- use_long: self.use_long,
- };
- sub_help.write_args(&args, heading, option_sort_key);
- if subcommand.is_flatten_help_set() {
- sub_help.write_flat_subcommands(subcommand, first);
- }
+ // Help command
+ self.write_flat_subcommands_under_heading(&ord_v, None, first, true);
+ }
+
+ fn write_flat_subcommands_under_heading(
+ &mut self,
+ ord_v: &BTreeMap<(usize, &str), &Command>,
+ heading: Option<&str>,
+ first: &mut bool,
+ include_help: bool,
+ ) {
+ debug!("help_template::write subcommand under heading: `{heading:?}`");
+ // If a custom heading is set ignore the include help flag
+ let bt: Vec<(&(usize, &str), &&Command)> = if heading.is_some() {
+ ord_v
+ .iter()
+ .filter(|item| item.1.get_help_heading() == heading)
+ .collect()
+ } else {
+ ord_v
+ .iter()
+ .filter(|item| item.1.get_help_heading() == heading)
+ .filter(|item| {
+ if include_help {
+ item.1.get_name() == "help"
+ } else {
+ item.1.get_name() != "help"
+ }
+ })
+ .collect()
+ };
+
+ for (_, subcommand) in bt {
+ self.write_flat_subcommand(subcommand, first);
+ }
+ }
+
+ fn write_flat_subcommand(&mut self, subcommand: &Command, first: &mut bool) {
+ use std::fmt::Write as _;
+ let header = &self.styles.get_header();
+
+ if !*first {
+ self.writer.push_str("\n\n");
+ }
+
+ *first = false;
+
+ let heading = subcommand.get_usage_name_fallback();
+ let about = subcommand
+ .get_about()
+ .or_else(|| subcommand.get_long_about())
+ .unwrap_or_default();
+
+ let _ = write!(self.writer, "{header}{heading}:{header:#}",);
+ if !about.is_empty() {
+ let _ = write!(self.writer, "\n{about}",);
+ }
+
+ let args = subcommand
+ .get_arguments()
+ .filter(|arg| should_show_arg(self.use_long, arg) && !arg.is_global_set())
+ .collect::>();
+ if !args.is_empty() {
+ self.writer.push_str("\n");
+ }
+
+ let mut sub_help = HelpTemplate {
+ writer: self.writer,
+ cmd: subcommand,
+ styles: self.styles,
+ usage: self.usage,
+ next_line_help: self.next_line_help,
+ term_w: self.term_w,
+ use_long: self.use_long,
+ };
+ sub_help.write_args(&args, heading, option_sort_key);
+ if subcommand.is_flatten_help_set() {
+ sub_help.write_flat_subcommands(subcommand, first);
}
}
@@ -963,7 +1012,39 @@ impl HelpTemplate<'_, '_> {
let next_line_help = self.will_subcommands_wrap(cmd.get_subcommands(), longest);
- for (i, (sc_str, sc)) in ord_v.into_iter().enumerate() {
+ let custom_sc_headings = self.cmd.get_subcommand_custom_help_headings();
+
+ // User commands without heading
+ self.write_subcommands_under_heading(&ord_v, None, next_line_help, longest);
+
+ // User commands with heading
+ for heading in custom_sc_headings {
+ self.write_subcommands_under_heading(&ord_v, Some(heading), next_line_help, longest);
+ }
+ }
+
+ fn write_subcommands_under_heading(
+ &mut self,
+ ord_v: &BTreeMap<(usize, StyledStr), &Command>,
+ heading: Option<&str>,
+ next_line_help: bool,
+ longest: usize,
+ ) {
+ debug!("help_template::write subcommand under heading: `{heading:?}`");
+ use std::fmt::Write as _;
+ let header = &self.styles.get_header();
+
+ if let Some(heading) = heading {
+ self.writer.push_str("\n\n");
+ let _ = write!(self.writer, "{header}{heading}:{header:#}\n",);
+ }
+
+ for (i, (sc_str, sc)) in ord_v
+ .clone()
+ .into_iter()
+ .filter(|item| item.1.get_help_heading() == heading)
+ .enumerate()
+ {
if 0 < i {
self.writer.push_str("\n");
}
diff --git a/clap_builder/src/output/usage.rs b/clap_builder/src/output/usage.rs
index 7c4ddc5ffca..db341c12f00 100644
--- a/clap_builder/src/output/usage.rs
+++ b/clap_builder/src/output/usage.rs
@@ -113,12 +113,49 @@ impl Usage<'_> {
}
let mut cmd = self.cmd.clone();
cmd.build();
- for (i, sub) in cmd
+
+ let mut first = true;
+ // Get the custom headings
+ let custom_sc_headings = cmd.get_subcommand_custom_help_headings();
+
+ // Write commands without headings
+ for sub in cmd
.get_subcommands()
.filter(|c| !c.is_hide_set())
- .enumerate()
+ .filter(|c| c.get_help_heading().is_none())
+ .filter(|c| c.get_name() != "help")
{
- if i != 0 {
+ if sub.get_name() == "help" {
+ continue;
+ }
+ if !first {
+ styled.trim_end();
+ let _ = write!(styled, "{USAGE_SEP}");
+ }
+ first = false;
+ Usage::new(sub).write_usage_no_title(styled, &[]);
+ }
+
+ // Write commands with headings
+ for heading in custom_sc_headings {
+ for sub in cmd
+ .get_subcommands()
+ .filter(|c| !c.is_hide_set())
+ .filter(|c| c.get_help_heading() == Some(heading))
+ .filter(|c| c.get_name() != "help")
+ {
+ if !first {
+ styled.trim_end();
+ let _ = write!(styled, "{USAGE_SEP}");
+ }
+ first = false;
+ Usage::new(sub).write_usage_no_title(styled, &[]);
+ }
+ }
+
+ // Write help command last (regardless of custom headings)
+ if let Some(sub) = cmd.find_subcommand("help") {
+ if !first {
styled.trim_end();
let _ = write!(styled, "{USAGE_SEP}");
}
diff --git a/clap_builder/src/util/color.rs b/clap_builder/src/util/color.rs
index c1978a38400..cd02b9494c1 100644
--- a/clap_builder/src/util/color.rs
+++ b/clap_builder/src/util/color.rs
@@ -2,7 +2,7 @@ use crate::builder::PossibleValue;
use crate::derive::ValueEnum;
/// Represents the color preferences for program output
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum ColorChoice {
/// Enables colored output only when the output is going to a terminal or TTY.
///
@@ -23,6 +23,7 @@ pub enum ColorChoice {
/// .get_matches();
/// # }
/// ```
+ #[default]
Auto,
/// Enables colored output regardless of whether or not the output is going to a terminal/TTY.
@@ -65,12 +66,6 @@ impl ColorChoice {
}
}
-impl Default for ColorChoice {
- fn default() -> Self {
- Self::Auto
- }
-}
-
impl std::fmt::Display for ColorChoice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
diff --git a/clap_complete/CHANGELOG.md b/clap_complete/CHANGELOG.md
index 0d440e341d5..31fcc7d9006 100644
--- a/clap_complete/CHANGELOG.md
+++ b/clap_complete/CHANGELOG.md
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased] - ReleaseDate
+## [4.5.61] - 2025-11-19
+
+## [4.5.60] - 2025-10-29
+
+### Fixes
+
+- *(dynamic/powershell)* don't repeat previous words when completing
+
## [4.5.59] - 2025-10-13
## [4.5.58] - 2025-09-16
@@ -602,7 +610,9 @@ MSRV changed to 1.64.0
## [3.0.1] - 2022-01-03
-[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.59...HEAD
+[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.61...HEAD
+[4.5.61]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.60...clap_complete-v4.5.61
+[4.5.60]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.59...clap_complete-v4.5.60
[4.5.59]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.58...clap_complete-v4.5.59
[4.5.58]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.57...clap_complete-v4.5.58
[4.5.57]: https://github.com/clap-rs/clap/compare/clap_complete-v4.5.56...clap_complete-v4.5.57
diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml
index 5b0731a86fa..1b53c3db907 100644
--- a/clap_complete/Cargo.toml
+++ b/clap_complete/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clap_complete"
-version = "4.5.59"
+version = "4.5.61"
description = "Generate shell completion scripts for your clap::Command"
categories = ["command-line-interface"]
keywords = [
diff --git a/clap_complete/README.md b/clap_complete/README.md
index 48f7a169d0a..3fc3e640021 100644
--- a/clap_complete/README.md
+++ b/clap_complete/README.md
@@ -5,16 +5,16 @@
[](https://crates.io/crates/clap_complete)
[](https://crates.io/crates/clap_complete)
-[](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.59/LICENSE-APACHE)
-[](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.59/LICENSE-MIT)
+[](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.61/LICENSE-APACHE)
+[](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.61/LICENSE-MIT)
Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT).
1. [About](#about)
2. [API Reference](https://docs.rs/clap_complete)
3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions)
-4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.59/clap_complete/CONTRIBUTING.md)
-5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.59/README.md#sponsors)
+4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.61/clap_complete/CONTRIBUTING.md)
+5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_complete-v4.5.61/README.md#sponsors)
## About
diff --git a/clap_complete_nushell/CHANGELOG.md b/clap_complete_nushell/CHANGELOG.md
index f04f121dfad..c65da047ff0 100644
--- a/clap_complete_nushell/CHANGELOG.md
+++ b/clap_complete_nushell/CHANGELOG.md
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased] - ReleaseDate
+## [4.5.10] - 2025-10-31
+
+### Fixes
+
+- Fix the order of positional arguments
+
## [4.5.9] - 2025-10-13
## [4.5.8] - 2025-07-09
@@ -60,7 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
## [4.3.0] - 2023-05-24
-[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.9...HEAD
+[Unreleased]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.10...HEAD
+[4.5.10]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.9...clap_complete_nushell-v4.5.10
[4.5.9]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.8...clap_complete_nushell-v4.5.9
[4.5.8]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.7...clap_complete_nushell-v4.5.8
[4.5.7]: https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.6...clap_complete_nushell-v4.5.7
diff --git a/clap_complete_nushell/Cargo.toml b/clap_complete_nushell/Cargo.toml
index 790d48359a2..2cf7a7dbb52 100644
--- a/clap_complete_nushell/Cargo.toml
+++ b/clap_complete_nushell/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clap_complete_nushell"
-version = "4.5.9"
+version = "4.5.10"
description = "A generator library used with clap for Nushell completion scripts"
categories = ["command-line-interface"]
keywords = [
diff --git a/clap_complete_nushell/src/lib.rs b/clap_complete_nushell/src/lib.rs
index d0c08540460..9939bf417a9 100644
--- a/clap_complete_nushell/src/lib.rs
+++ b/clap_complete_nushell/src/lib.rs
@@ -219,7 +219,16 @@ fn generate_completion(completions: &mut String, cmd: &Command, is_subcommand: b
completions.push_str(format!(" export extern {name} [\n").as_str());
}
- for arg in cmd.get_arguments() {
+ let flags: Vec<_> = cmd.get_arguments().filter(|a| !a.is_positional()).collect();
+ let mut positionals: Vec<_> = cmd.get_positionals().collect();
+
+ positionals.sort_by_key(|arg| arg.get_index());
+
+ for arg in flags {
+ append_argument(arg, name, completions);
+ }
+
+ for arg in positionals {
append_argument(arg, name, completions);
}
diff --git a/clap_complete_nushell/tests/common.rs b/clap_complete_nushell/tests/common.rs
index 0c38f0d0c4a..4d5bd02f215 100644
--- a/clap_complete_nushell/tests/common.rs
+++ b/clap_complete_nushell/tests/common.rs
@@ -168,6 +168,29 @@ pub(crate) fn sub_subcommands_command(name: &'static str) -> Command {
)
}
+pub(crate) fn positional_index_command(name: &'static str) -> Command {
+ Command::new(name)
+ .version("3.0")
+ .about("Tests positional argument index ordering")
+ .arg(
+ Arg::new("flag")
+ .short('f')
+ .long("flag")
+ .action(ArgAction::SetTrue)
+ .help("some flag"),
+ )
+ .arg(Arg::new("third").index(3).help("third positional"))
+ .arg(Arg::new("first").index(1).help("first positional"))
+ .arg(
+ Arg::new("option")
+ .short('o')
+ .long("option")
+ .action(ArgAction::Set)
+ .help("some option"),
+ )
+ .arg(Arg::new("second").index(2).help("second positional"))
+}
+
pub(crate) fn value_hint_command(name: &'static str) -> Command {
Command::new(name)
.arg(
diff --git a/clap_complete_nushell/tests/nushell.rs b/clap_complete_nushell/tests/nushell.rs
index f3eacd85c0e..a05d2d482d6 100644
--- a/clap_complete_nushell/tests/nushell.rs
+++ b/clap_complete_nushell/tests/nushell.rs
@@ -83,3 +83,15 @@ fn value_hint() {
name,
);
}
+
+#[test]
+fn positional_index() {
+ let name = "my-app";
+ let cmd = common::positional_index_command(name);
+ common::assert_matches(
+ snapbox::file!["snapshots/positional_index.nu"],
+ clap_complete_nushell::Nushell,
+ cmd,
+ name,
+ );
+}
diff --git a/clap_complete_nushell/tests/snapshots/aliases.nu b/clap_complete_nushell/tests/snapshots/aliases.nu
index 8263e60315e..67a70d6ff85 100644
--- a/clap_complete_nushell/tests/snapshots/aliases.nu
+++ b/clap_complete_nushell/tests/snapshots/aliases.nu
@@ -8,9 +8,9 @@ module completions {
--option(-o): string # cmd option
--opt: string # cmd option
-O: string # cmd option
- positional?: string
--help(-h) # Print help
--version(-V) # Print version
+ positional?: string
]
}
diff --git a/clap_complete_nushell/tests/snapshots/feature_sample.nu b/clap_complete_nushell/tests/snapshots/feature_sample.nu
index eb081b9b4de..912ac28aa78 100644
--- a/clap_complete_nushell/tests/snapshots/feature_sample.nu
+++ b/clap_complete_nushell/tests/snapshots/feature_sample.nu
@@ -6,13 +6,13 @@ module completions {
# Tests completions
export extern my-app [
- file?: path # some input file
--config(-c) # some config file with another line
--conf # some config file with another line
-C # some config file with another line
- choice?: string@"nu-complete my-app choice"
--help(-h) # Print help
--version(-V) # Print version
+ file?: path # some input file
+ choice?: string@"nu-complete my-app choice"
]
# tests things
diff --git a/clap_complete_nushell/tests/snapshots/home/static/test/nu/.config/nushell/completions/test.nu b/clap_complete_nushell/tests/snapshots/home/static/test/nu/.config/nushell/completions/test.nu
index ac1e050f667..98e5762e4d8 100644
--- a/clap_complete_nushell/tests/snapshots/home/static/test/nu/.config/nushell/completions/test.nu
+++ b/clap_complete_nushell/tests/snapshots/home/static/test/nu/.config/nushell/completions/test.nu
@@ -111,10 +111,10 @@ module completions {
--delim: string
--tuple: string
--require-eq: string
- ...term: string
--global # everywhere
--help(-h) # Print help
--version(-V) # Print version
+ ...term: string
]
export extern "test pacman" [
@@ -150,11 +150,11 @@ module completions {
]
export extern "test last" [
- first?: string
- free?: string
--global # everywhere
--help(-h) # Print help
--version(-V) # Print version
+ first?: string
+ free?: string
]
export extern "test alias" [
@@ -164,10 +164,10 @@ module completions {
--option(-o): string # cmd option
--opt: string # cmd option
-O: string # cmd option
- positional?: string
--global # everywhere
--help(-h) # Print help
--version(-V) # Print version
+ positional?: string
]
def "nu-complete test hint choice" [] {
@@ -184,7 +184,6 @@ module completions {
--exe(-e): path
--cmd-name: string
--cmd(-c): string
- command_with_args?: string
--user(-u): string
--host(-H): string
--url: string
@@ -192,6 +191,7 @@ module completions {
--global # everywhere
--help(-h) # Print help
--version(-V) # Print version
+ command_with_args?: string
]
# Print this message or the help of the given subcommand(s)
diff --git a/clap_complete_nushell/tests/snapshots/positional_index.nu b/clap_complete_nushell/tests/snapshots/positional_index.nu
new file mode 100644
index 00000000000..126a92fae99
--- /dev/null
+++ b/clap_complete_nushell/tests/snapshots/positional_index.nu
@@ -0,0 +1,16 @@
+module completions {
+
+ # Tests positional argument index ordering
+ export extern my-app [
+ --flag(-f) # some flag
+ --option(-o): string # some option
+ --help(-h) # Print help
+ --version(-V) # Print version
+ first?: string # first positional
+ second?: string # second positional
+ third?: string # third positional
+ ]
+
+}
+
+export use completions *
diff --git a/clap_complete_nushell/tests/snapshots/special_commands.nu b/clap_complete_nushell/tests/snapshots/special_commands.nu
index 0551531b3f7..6b6541d0786 100644
--- a/clap_complete_nushell/tests/snapshots/special_commands.nu
+++ b/clap_complete_nushell/tests/snapshots/special_commands.nu
@@ -6,13 +6,13 @@ module completions {
# Tests completions
export extern my-app [
- file?: path # some input file
--config(-c) # some config file with another line
--conf # some config file with another line
-C # some config file with another line
- choice?: string@"nu-complete my-app choice"
--help(-h) # Print help
--version(-V) # Print version
+ file?: path # some input file
+ choice?: string@"nu-complete my-app choice"
]
# tests things
@@ -25,9 +25,9 @@ module completions {
# tests other things
export extern "my-app some_cmd" [
--config: string # the other case to test
- ...path: string
--help(-h) # Print help
--version(-V) # Print version
+ ...path: string
]
export extern "my-app some-cmd-with-hyphens" [
diff --git a/clap_complete_nushell/tests/snapshots/sub_subcommands.nu b/clap_complete_nushell/tests/snapshots/sub_subcommands.nu
index 2f56ca6d46c..a17626202c9 100644
--- a/clap_complete_nushell/tests/snapshots/sub_subcommands.nu
+++ b/clap_complete_nushell/tests/snapshots/sub_subcommands.nu
@@ -6,13 +6,13 @@ module completions {
# Tests completions
export extern my-app [
- file?: path # some input file
--config(-c) # some config file with another line
--conf # some config file with another line
-C # some config file with another line
- choice?: string@"nu-complete my-app choice"
--help(-h) # Print help
--version(-V) # Print version
+ file?: path # some input file
+ choice?: string@"nu-complete my-app choice"
]
# tests things
diff --git a/clap_complete_nushell/tests/snapshots/value_hint.nu b/clap_complete_nushell/tests/snapshots/value_hint.nu
index f27849a5463..f3d2a470596 100644
--- a/clap_complete_nushell/tests/snapshots/value_hint.nu
+++ b/clap_complete_nushell/tests/snapshots/value_hint.nu
@@ -14,12 +14,12 @@ module completions {
--exe(-e): path
--cmd-name: string
--cmd(-c): string
- command_with_args?: string
--user(-u): string
--host(-H): string
--url: string
--email: string
--help(-h) # Print help
+ command_with_args?: string
]
}
diff --git a/examples/tutorial_builder/04_04_custom.rs b/examples/tutorial_builder/04_04_custom.rs
index 840b3aa4f71..98019dc71fe 100644
--- a/examples/tutorial_builder/04_04_custom.rs
+++ b/examples/tutorial_builder/04_04_custom.rs
@@ -37,7 +37,7 @@ fn main() {
)
.exit();
}
- ver.to_string()
+ ver.clone()
} else {
// Increment the one requested (in a real program, we'd reset the lower numbers)
let (maj, min, pat) = (
diff --git a/tests/builder/help_heading.rs b/tests/builder/help_heading.rs
new file mode 100644
index 00000000000..68e4a1f5de6
--- /dev/null
+++ b/tests/builder/help_heading.rs
@@ -0,0 +1,2849 @@
+#![cfg(feature = "help")]
+
+use clap::{Arg, Command};
+
+use super::utils;
+
+fn setup() -> Command {
+ Command::new("test")
+ .author("Kevin K.")
+ .about("tests stuff")
+ .version("1.3")
+}
+
+fn empty_args() -> impl IntoIterator- {
+ std::iter::empty()
+}
+
+#[test]
+fn test_multiple_commands_mixed_standard_and_custom_headers() {
+ static VISIBLE_ALIAS_HELP: &str = "\
+Usage: clap-test [COMMAND]
+
+Help Section:
+ help Print this message or the help of the given subcommand(s)
+
+Commands:
+ def_cmd1 First command under default command heading
+ def_cmd4 Fourth command under default command heading
+ def_cmd3 Third command under default command heading
+ def_cmd2 Second command under default command heading
+
+First Custom:
+ ch1_cmd1 First command under first custom command heading
+ ch1_cmd2 Second command under first custom command heading
+ ch1_cmd4 Fourth command under first custom command heading
+ ch1_cmd3 Third command under first custom command heading
+
+Second Custom:
+ ch2_cmd3 Third command under second custom command heading
+ ch2_cmd4 Fourth command under second custom command heading
+ ch2_cmd2 Second command under second custom command heading
+ ch2_cmd1 First command under second custom command heading
+
+Options:
+ -h, --help Print help
+ -V, --version Print version
+";
+
+ let cmd = Command::new("clap-test")
+ .version("2.6")
+ .subcommand_help_heading("Help Section")
+ .subcommand(
+ Command::new("def_cmd1")
+ .about("First command under default command heading")
+ .help_heading("Commands"),
+ )
+ .subcommand(
+ Command::new("def_cmd4")
+ .about("Fourth command under default command heading")
+ .help_heading("Commands"),
+ )
+ .subcommand(
+ Command::new("def_cmd3")
+ .about("Third command under default command heading")
+ .help_heading("Commands"),
+ )
+ .subcommand(
+ Command::new("def_cmd2")
+ .about("Second command under default command heading")
+ .help_heading("Commands"),
+ )
+ .subcommand(
+ Command::new("ch1_cmd1")
+ .about("First command under first custom command heading")
+ .help_heading("First Custom"),
+ )
+ .subcommand(
+ Command::new("ch2_cmd3")
+ .about("Third command under second custom command heading")
+ .help_heading("Second Custom"),
+ )
+ .subcommand(
+ Command::new("ch1_cmd2")
+ .about("Second command under first custom command heading")
+ .help_heading("First Custom"),
+ )
+ .subcommand(
+ Command::new("ch1_cmd4")
+ .about("Fourth command under first custom command heading")
+ .help_heading("First Custom"),
+ )
+ .subcommand(
+ Command::new("ch2_cmd4")
+ .about("Fourth command under second custom command heading")
+ .help_heading("Second Custom"),
+ )
+ .subcommand(
+ Command::new("ch2_cmd2")
+ .about("Second command under second custom command heading")
+ .help_heading("Second Custom"),
+ )
+ .subcommand(
+ Command::new("ch2_cmd1")
+ .about("First command under second custom command heading")
+ .help_heading("Second Custom"),
+ )
+ .subcommand(
+ Command::new("ch1_cmd3")
+ .about("Third command under first custom command heading")
+ .help_heading("First Custom"),
+ );
+
+ utils::assert_output(cmd, "clap-test --help", VISIBLE_ALIAS_HELP, false);
+ }
+
+#[test]
+fn test_multiple_commands_mixed_headings_flatten() {
+ static VISIBLE_ALIAS_HELP: &str = "\
+Usage: clap-test
+ clap-test def_cmd1
+ clap-test def_cmd2
+ clap-test cust_cmd1
+ clap-test cust_cmd2 --child
+ clap-test other_cmd1
+ clap-test other_cmd2 --child
+ clap-test help [COMMAND]...
+
+Options:
+ -h, --help Print help
+ -V, --version Print version
+
+clap-test def_cmd1:
+Def_cmd1 under default command heading
+ -h, --help Print help
+
+clap-test def_cmd2:
+Def_cmd2 under default command heading
+ -h, --help Print help
+
+clap-test cust_cmd1:
+Cust_cmd1 under custom command heading
+ -h, --help Print help
+
+clap-test cust_cmd2:
+Cust_cmd2 under custom command heading
+ --child child help
+ -h, --help Print help
+
+clap-test other_cmd1:
+Other_cmd1 under other command heading
+ -h, --help Print help
+
+clap-test other_cmd2:
+Other_cmd2 under other command heading
+ --child
+ -h, --help Print help
+
+clap-test help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+";
+
+ let cmd = Command::new("clap-test")
+ .version("2.6")
+ .flatten_help(true)
+ .subcommand(
+ Command::new("def_cmd1")
+ .about("Def_cmd1 under default command heading")
+ )
+ .subcommand(
+ Command::new("cust_cmd1")
+ .about("Cust_cmd1 under custom command heading")
+ .help_heading("Custom Heading"),
+ )
+ .subcommand(
+ Command::new("other_cmd1")
+ .about("Other_cmd1 under other command heading")
+ .help_heading("Other Heading"),
+ )
+ .subcommand(
+ Command::new("other_cmd2")
+ .about("Other_cmd2 under other command heading")
+ .help_heading("Other Heading")
+ .arg(Arg::new("child").long("child").required(true)),
+ )
+ .subcommand(
+ Command::new("def_cmd2")
+ .about("Def_cmd2 under default command heading")
+ )
+ .subcommand(
+ Command::new("cust_cmd2")
+ .about("Cust_cmd2 under custom command heading")
+ .help_heading("Custom Heading")
+ .arg(Arg::new("child").long("child").required(true).help("child help")),
+ );
+
+ utils::assert_output(cmd, "clap-test --help", VISIBLE_ALIAS_HELP, false);
+}
+
+
+#[test]
+fn test_help_header_hide_commands_header() {
+ static VISIBLE_ALIAS_HELP: &str = "\
+Usage: clap-test [COMMAND]
+
+Test commands:
+ test Some help
+
+Options:
+ -h, --help Print help
+ -V, --version Print version
+";
+
+ let cmd = Command::new("clap-test")
+ .version("2.6")
+ .disable_help_subcommand(true)
+ .subcommand_help_heading("Test commands")
+ .subcommand(Command::new("test").about("Some help"));
+ utils::assert_output(cmd, "clap-test --help", VISIBLE_ALIAS_HELP, false);
+}
+
+
+
+mod adjusted_help{
+
+
+// This file is created from a copy of help.rs
+// Any test not containing a subcommand is discarded
+// Any test with subcommand has help_heading added
+
+use clap::{arg, error::ErrorKind, Arg, ArgAction, Command};
+use snapbox::assert_data_eq;
+use snapbox::str;
+
+use super::utils;
+use super::setup;
+use super::empty_args;
+
+
+#[test]
+fn help_subcommand() {
+ let m = setup()
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("tests things")
+ .arg(arg!(-v --verbose "with verbosity")),
+ )
+ .try_get_matches_from(vec!["myprog", "help"]);
+
+ assert!(m.is_err());
+ assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp);
+}
+
+#[test]
+#[cfg(feature = "error-context")]
+fn help_multi_subcommand_error() {
+ let cmd = Command::new("ctest").subcommand(
+ Command::new("subcmd").subcommand(
+ Command::new("multi")
+ .help_heading("Custom Heading")
+ .about("tests subcommands")
+ .author("Kevin K. ")
+ .version("0.1")
+ .arg(arg!(
+ -f --flag "tests flags"
+ ))
+ .arg(
+ arg!(
+ -o --option "tests options"
+ )
+ .required(false)
+ .num_args(1..)
+ .action(ArgAction::Append),
+ ),
+ ),
+ );
+ let err = cmd
+ .try_get_matches_from(["ctest", "help", "subcmd", "multi", "foo"])
+ .unwrap_err();
+
+ assert_data_eq!(
+ err.to_string(),
+ str![[r#"
+error: unrecognized subcommand 'foo'
+
+Usage: ctest subcmd multi [OPTIONS]
+
+For more information, try '--help'.
+
+"#]]
+ );
+}
+
+#[test]
+fn subcommand_short_help() {
+ let m = utils::complex_app_with_help_heading().try_get_matches_from(vec!["clap-test", "subcmd", "-h"]);
+
+ assert!(m.is_err());
+ assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp);
+}
+
+#[test]
+fn subcommand_long_help() {
+ let m = utils::complex_app_with_help_heading().try_get_matches_from(vec!["clap-test", "subcmd", "--help"]);
+
+ assert!(m.is_err());
+ assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp);
+}
+
+#[test]
+fn subcommand_help_rev() {
+ let m = utils::complex_app_with_help_heading().try_get_matches_from(vec!["clap-test", "help", "subcmd"]);
+
+ assert!(m.is_err());
+ assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+fn complex_help_output() {
+ let expected = str![[r#"
+clap-test v1.4.8
+Kevin K.
+tests clap library
+
+Usage: clap-test [OPTIONS] [positional] [positional2] [positional3]... [COMMAND]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ subcmd tests subcommands
+
+Arguments:
+ [positional] tests positionals
+ [positional2] tests positionals with exclusions
+ [positional3]... tests specific values [possible values: vi, emacs]
+
+Options:
+ -o, --option ... tests options
+ -f, --flag... tests flags
+ -F tests flags with exclusions
+ --long-option-2 tests long options with exclusions
+ -O, --option3 specific vals [possible values: fast, slow]
+ --multvals Tests multiple values, not mult occs
+ --multvalsmo Tests multiple values, and mult occs
+ --minvals2 ... Tests 2 min vals
+ --maxvals3 ... Tests 3 max vals
+ --optvaleq[=] Tests optional value, require = sign
+ --optvalnoeq [] Tests optional value
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(utils::complex_app_with_help_heading(), "clap-test --help", expected, false);
+}
+
+#[test]
+fn multi_level_sc_help() {
+ let cmd = Command::new("ctest").subcommand(
+ Command::new("subcmd")
+ .help_heading("First Heading")
+ .subcommand(
+ Command::new("multi")
+ .about("tests subcommands")
+ .help_heading("Second Heading")
+ .author("Kevin K. ")
+ .version("0.1")
+ .arg(arg!(
+ -f --flag "tests flags"
+ ))
+ .arg(
+ arg!(
+ -o --option "tests options"
+ )
+ .required(false)
+ .num_args(1..)
+ .action(ArgAction::Append),
+ ),
+ ),
+ );
+
+ let expected = str![[r#"
+tests subcommands
+
+Usage: ctest subcmd multi [OPTIONS]
+
+Options:
+ -f, --flag tests flags
+ -o, --option ... tests options
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(cmd, "ctest help subcmd multi", expected, false);
+}
+
+#[test]
+fn no_wrap_default_help() {
+ let cmd = Command::new("ctest").version("1.0").term_width(0);
+
+ let expected = str![[r#"
+Usage: ctest
+
+Options:
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(cmd, "ctest --help", expected, false);
+}
+
+
+#[test]
+fn try_help_subcommand_default() {
+ let cmd = Command::new("ctest")
+ .version("1.0")
+ .subcommand(Command::new("foo").help_heading("Custom Heading"))
+ .term_width(0);
+
+ let expected = str![[r#"
+error: unrecognized subcommand 'bar'
+
+Usage: ctest [COMMAND]
+
+For more information, try '--help'.
+
+"#]];
+ utils::assert_output(cmd, "ctest bar", expected, true);
+}
+
+#[test]
+fn try_help_subcommand_custom_flag() {
+ let cmd = Command::new("ctest")
+ .version("1.0")
+ .disable_help_flag(true)
+ .arg(
+ Arg::new("help")
+ .long("help")
+ .short('h')
+ .action(ArgAction::Help)
+ .global(true),
+ )
+ .subcommand(Command::new("foo").help_heading("Custom Heading"))
+ .term_width(0);
+
+ let expected = str![[r#"
+error: unrecognized subcommand 'bar'
+
+Usage: ctest [COMMAND]
+
+For more information, try '--help'.
+
+"#]];
+ utils::assert_output(cmd, "ctest bar", expected, true);
+}
+
+#[test]
+fn try_help_subcommand_custom_flag_no_action() {
+ let cmd = Command::new("ctest")
+ .version("1.0")
+ .disable_help_flag(true)
+ // Note `ArgAction::Help` is excluded
+ .arg(Arg::new("help").long("help").global(true))
+ .subcommand(Command::new("foo").help_heading("Custom Heading"))
+ .term_width(0);
+
+ let expected = str![[r#"
+error: unrecognized subcommand 'bar'
+
+Usage: ctest [COMMAND]
+
+For more information, try 'help'.
+
+"#]];
+ utils::assert_output(cmd, "ctest bar", expected, true);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+#[cfg(feature = "wrap_help")]
+fn wrapped_help() {
+ let cmd = Command::new("test")
+ .term_width(67)
+ .arg(
+ Arg::new("all")
+ .short('a')
+ .long("all")
+ .action(ArgAction::SetTrue)
+ .help("Also do versioning for private crates (will not be published)"),
+ )
+ .arg(
+ Arg::new("exact")
+ .long("exact")
+ .action(ArgAction::SetTrue)
+ .help("Specify inter dependency version numbers exactly with `=`"),
+ )
+ .arg(
+ Arg::new("no_git_commit")
+ .long("no-git-commit")
+ .action(ArgAction::SetTrue)
+ .help("Do not commit version changes"),
+ )
+ .arg(
+ Arg::new("no_git_push")
+ .long("no-git-push")
+ .action(ArgAction::SetTrue)
+ .help("Do not push generated commit and tags to git remote"),
+ )
+ .subcommand(
+ Command::new("sub1")
+ .help_heading("Custom Heading")
+ .about("One two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen")
+ );
+
+ let expected = str![[r#"
+Usage: test [OPTIONS] [COMMAND]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ sub1 One two three four five six seven eight nine ten eleven
+ twelve thirteen fourteen fifteen
+
+Options:
+ -a, --all Also do versioning for private crates (will
+ not be published)
+ --exact Specify inter dependency version numbers
+ exactly with `=`
+ --no-git-commit Do not commit version changes
+ --no-git-push Do not push generated commit and tags to git
+ remote
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "test --help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+#[cfg(feature = "wrap_help")]
+fn unwrapped_help() {
+ let cmd = Command::new("test")
+ .term_width(68)
+ .arg(
+ Arg::new("all")
+ .short('a')
+ .long("all")
+ .action(ArgAction::SetTrue)
+ .help("Also do versioning for private crates (will not be published)"),
+ )
+ .arg(
+ Arg::new("exact")
+ .long("exact")
+ .action(ArgAction::SetTrue)
+ .help("Specify inter dependency version numbers exactly with `=`"),
+ )
+ .arg(
+ Arg::new("no_git_commit")
+ .long("no-git-commit")
+ .action(ArgAction::SetTrue)
+ .help("Do not commit version changes"),
+ )
+ .arg(
+ Arg::new("no_git_push")
+ .long("no-git-push")
+ .action(ArgAction::SetTrue)
+ .help("Do not push generated commit and tags to git remote"),
+ )
+ .subcommand(
+ Command::new("sub1")
+ .help_heading("Custom Heading")
+ .about("One two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen")
+ );
+
+ let expected = str![[r#"
+Usage: test [OPTIONS] [COMMAND]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ sub1 One two three four five six seven eight nine ten eleven
+ twelve thirteen fourteen fifteen
+
+Options:
+ -a, --all Also do versioning for private crates (will
+ not be published)
+ --exact Specify inter dependency version numbers
+ exactly with `=`
+ --no-git-commit Do not commit version changes
+ --no-git-push Do not push generated commit and tags to git
+ remote
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "test --help", expected, false);
+}
+
+#[test]
+fn complex_subcommand_help_output() {
+ let a = utils::complex_app_with_help_heading();
+
+ let expected = str![[r#"
+clap-test-subcmd 0.1
+Kevin K.
+tests subcommands
+
+Usage: clap-test subcmd [OPTIONS] [scpositional]
+
+Arguments:
+ [scpositional] tests positionals
+
+Options:
+ -o, --option ... tests options
+ -f, --flag... tests flags
+ -s, --subcmdarg tests other args
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(a, "clap-test subcmd --help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[cfg(feature = "wrap_help")]
+fn setup_aliases_with_help_heading() -> Command {
+ Command::new("ctest")
+ .version("0.1")
+ .arg(
+ Arg::new("dest")
+ .short('d')
+ .long("destination")
+ .value_name("FILE")
+ .help("File to save into")
+ .long_help("The Filepath to save into the result")
+ .short_alias('q')
+ .short_aliases(['w', 'e'])
+ .alias("arg-alias")
+ .aliases(["do-stuff", "do-tests"])
+ .visible_short_alias('t')
+ .visible_short_aliases(['i', 'o'])
+ .visible_alias("file")
+ .visible_aliases(["into", "to"])
+ .action(ArgAction::Set),
+ )
+ .subcommand(
+ Command::new("rev")
+ .help_heading("Custom Heading")
+ .short_flag('r')
+ .long_flag("inplace")
+ .about("In place")
+ .long_about("Change mode to work in place on source")
+ .alias("subc-alias")
+ .aliases(["subc-do-stuff", "subc-do-tests"])
+ .short_flag_alias('j')
+ .short_flag_aliases(['k', 'l'])
+ .long_flag_alias("subc-long-flag-alias")
+ .long_flag_aliases(["subc-long-do-stuff", "subc-long-do-tests"])
+ .visible_alias("source")
+ .visible_aliases(["from", "onsource"])
+ .visible_short_flag_alias('s')
+ .visible_short_flag_aliases(['f', 'g'])
+ .visible_long_flag_alias("origin")
+ .visible_long_flag_aliases(["path", "tryfrom"])
+ .arg(
+ Arg::new("input")
+ .value_name("INPUT")
+ .help("The source file"),
+ ),
+ )
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+#[cfg(feature = "wrap_help")]
+fn visible_aliases_with_short_help() {
+ let app = setup_aliases_with_help_heading().term_width(80);
+
+ let expected = str![[r#"
+Usage: ctest [OPTIONS] [COMMAND]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ rev, -r, --inplace In place [aliases: -s, -f, -g, --origin, --path,
+ --tryfrom, source, from, onsource]
+
+Options:
+ -d, --destination File to save into [aliases: -t, -i, -o, --file,
+ --into, --to]
+ -h, --help Print help (see more with '--help')
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(app, "ctest -h", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+#[cfg(feature = "wrap_help")]
+fn visible_aliases_with_long_help() {
+ let app = setup_aliases_with_help_heading().term_width(80);
+
+ let expected = str![[r#"
+Usage: ctest [OPTIONS] [COMMAND]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ rev, -r, --inplace In place [aliases: -s, -f, -g, --origin, --path,
+ --tryfrom, source, from, onsource]
+
+Options:
+ -d, --destination
+ The Filepath to save into the result
+
+ [aliases: -t, -i, -o, --file, --into, --to]
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+ -V, --version
+ Print version
+
+"#]];
+ utils::assert_output(app, "ctest --help", expected, false);
+}
+
+#[test]
+#[cfg(feature = "wrap_help")]
+fn dont_wrap_urls() {
+ let cmd = Command::new("Example")
+ .term_width(30)
+ .subcommand(Command::new("update")
+ .help_heading("Custom Heading")
+ .arg(
+ Arg::new("force-non-host")
+ .help("Install toolchains that require an emulator. See https://github.com/rust-lang/rustup/wiki/Non-host-toolchains")
+ .long("force-non-host")
+ .action(ArgAction::SetTrue))
+ );
+
+ let expected = str![[r#"
+Usage: Example update [OPTIONS]
+
+Options:
+ --force-non-host
+ Install toolchains
+ that require an
+ emulator. See
+ https://github.com/rust-lang/rustup/wiki/Non-host-toolchains
+ -h, --help
+ Print help
+
+"#]];
+ utils::assert_output(cmd, "Example update --help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+fn sc_negates_reqs() {
+ let cmd = Command::new("prog")
+ .version("1.0")
+ .subcommand_negates_reqs(true)
+ .arg(arg!(-o --opt "tests options").required(true))
+ .arg(Arg::new("PATH").help("help"))
+ .subcommand(Command::new("test").help_heading("Custom Heading"));
+
+ let expected = str![[r#"
+Usage: prog --opt [PATH]
+ prog [PATH]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ test
+
+Arguments:
+ [PATH] help
+
+Options:
+ -o, --opt tests options
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(cmd, "prog --help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+fn args_negate_sc() {
+ let cmd = Command::new("prog")
+ .version("1.0")
+ .args_conflicts_with_subcommands(true)
+ .arg(arg!(-f --flag "testing flags"))
+ .arg(arg!(-o --opt "tests options"))
+ .arg(Arg::new("PATH").help("help"))
+ .subcommand(Command::new("test").help_heading("Custom Heading"));
+
+ let expected = str![[r#"
+Usage: prog [OPTIONS] [PATH]
+ prog
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ test
+
+Arguments:
+ [PATH] help
+
+Options:
+ -f, --flag testing flags
+ -o, --opt tests options
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(cmd, "prog --help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+fn dont_strip_padding_issue_5083() {
+ let cmd = Command::new("test")
+ .help_template("{subcommands}")
+ .subcommands([
+ Command::new("one"),
+ Command::new("two").help_heading("Custom Heading"),
+ Command::new("three"),
+ ]);
+
+ let expected = str![[r#"
+ one
+ three
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ two
+
+"#]];
+ utils::assert_output(cmd, "test --help", expected, false);
+}
+
+
+// Updated to include the custom heading in the expected output
+#[test]
+fn last_arg_mult_usage_req_with_sc() {
+ let cmd = Command::new("last")
+ .version("0.1")
+ .subcommand_negates_reqs(true)
+ .arg(Arg::new("TARGET").required(true).help("some"))
+ .arg(Arg::new("CORPUS").help("some"))
+ .arg(
+ Arg::new("ARGS")
+ .action(ArgAction::Set)
+ .num_args(1..)
+ .last(true)
+ .required(true)
+ .help("some"),
+ )
+ .subcommand(Command::new("test").help_heading("Custom Heading").about("some"));
+
+ let expected = str![[r#"
+Usage: last [CORPUS] -- ...
+ last [TARGET] [CORPUS]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ test some
+
+Arguments:
+ some
+ [CORPUS] some
+ ... some
+
+Options:
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(cmd, "last --help", expected, false);
+}
+
+#[test]
+fn last_arg_mult_usage_with_sc() {
+ let cmd = Command::new("last")
+ .version("0.1")
+ .args_conflicts_with_subcommands(true)
+ .arg(Arg::new("TARGET").required(true).help("some"))
+ .arg(Arg::new("CORPUS").help("some"))
+ .arg(
+ Arg::new("ARGS")
+ .action(ArgAction::Set)
+ .num_args(1..)
+ .last(true)
+ .help("some"),
+ )
+ .subcommand(Command::new("test").help_heading("Custom Heading").about("some"));
+
+ let expected = str![[r#"
+Usage: last [CORPUS] [-- ...]
+ last
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ test some
+
+Arguments:
+ some
+ [CORPUS] some
+ [ARGS]... some
+
+Options:
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(cmd, "last --help", expected, false);
+}
+
+fn issue_1112_setup_help_heading() -> Command {
+ Command::new("test")
+ .version("1.3")
+ .disable_help_flag(true)
+ .arg(
+ Arg::new("help1")
+ .long("help")
+ .short('h')
+ .help("some help")
+ .action(ArgAction::SetTrue),
+ )
+ .subcommand(
+ Command::new("foo").arg(
+ Arg::new("help1")
+ .help_heading("Custom Heading")
+ .long("help")
+ .short('h')
+ .help("some help")
+ .action(ArgAction::SetTrue),
+ ),
+ )
+}
+
+#[test]
+fn prefer_user_help_long_1112() {
+ let m = issue_1112_setup_help_heading().try_get_matches_from(vec!["test", "--help"]);
+
+ assert!(m.is_ok(), "{}", m.unwrap_err());
+ let m = m.unwrap();
+ assert!(*m.get_one::("help1").expect("defaulted by clap"));
+}
+
+#[test]
+fn prefer_user_help_short_1112() {
+ let m = issue_1112_setup_help_heading().try_get_matches_from(vec!["test", "-h"]);
+
+ assert!(m.is_ok(), "{}", m.unwrap_err());
+ let m = m.unwrap();
+ assert!(*m.get_one::("help1").expect("defaulted by clap"));
+}
+
+#[test]
+fn prefer_user_subcmd_help_long_1112() {
+ let m = issue_1112_setup_help_heading().try_get_matches_from(vec!["test", "foo", "--help"]);
+
+ assert!(m.is_ok(), "{}", m.unwrap_err());
+ let m = m.unwrap();
+ assert!(*m
+ .subcommand_matches("foo")
+ .unwrap()
+ .get_one::("help1")
+ .expect("defaulted by clap"));
+}
+
+#[test]
+fn prefer_user_subcmd_help_short_1112() {
+ let m = issue_1112_setup_help_heading().try_get_matches_from(vec!["test", "foo", "-h"]);
+
+ assert!(m.is_ok(), "{}", m.unwrap_err());
+ let m = m.unwrap();
+ assert!(m
+ .subcommand_matches("foo")
+ .unwrap()
+ .get_one::("help1")
+ .expect("defaulted by clap"));
+}
+
+
+#[test]
+#[should_panic = "List of such arguments: delete"]
+fn help_required_globally() {
+ Command::new("myapp")
+ .help_expected(true)
+ .arg(Arg::new("foo").help("It does foo stuff"))
+ .subcommand(
+ Command::new("bar").help_heading("Custom Heading")
+ .arg(Arg::new("create").help("creates bar"))
+ .arg(Arg::new("delete")),
+ )
+ .try_get_matches_from(empty_args())
+ .unwrap();
+}
+
+#[cfg(debug_assertions)]
+#[test]
+#[should_panic = "Command::help_expected is enabled for the Command"]
+fn help_required_globally_but_not_given_for_subcommand() {
+ Command::new("myapp")
+ .help_expected(true)
+ .arg(Arg::new("foo").help("It does foo stuff"))
+ .subcommand(
+ Command::new("bar").help_heading("Custom Heading")
+ .arg(Arg::new("create").help("creates bar"))
+ .arg(Arg::new("delete")),
+ )
+ .try_get_matches_from(empty_args())
+ .unwrap();
+}
+
+#[test]
+fn help_required_and_given_for_subcommand() {
+ Command::new("myapp")
+ .help_expected(true)
+ .arg(Arg::new("foo").help("It does foo stuff"))
+ .subcommand(
+ Command::new("bar").help_heading("Custom Heading")
+ .arg(Arg::new("create").help("creates bar"))
+ .arg(Arg::new("delete").help("deletes bar")),
+ )
+ .try_get_matches_from(empty_args())
+ .unwrap();
+}
+
+#[test]
+fn help_subcmd_help() {
+ let cmd = Command::new("myapp")
+ .subcommand(Command::new("subcmd")
+ .help_heading("First Heading")
+ .subcommand(Command::new("multi")
+ .help_heading("Second Heading")
+ .version("1.0")));
+
+ let expected = str![[r#"
+Print this message or the help of the given subcommand(s)
+
+Usage: myapp help [COMMAND]...
+
+Arguments:
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd.clone(), "myapp help help", expected, false);
+}
+
+#[test]
+fn subcmd_help_subcmd_help() {
+ let cmd = Command::new("myapp")
+ .subcommand(Command::new("subcmd")
+ .help_heading("First Heading")
+ .subcommand(
+ Command::new("multi")
+ .help_heading("Second Heading")
+ .version("1.0")));
+
+ let expected = str![[r#"
+Print this message or the help of the given subcommand(s)
+
+Usage: myapp subcmd help [COMMAND]...
+
+Arguments:
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd.clone(), "myapp subcmd help help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+fn global_args_should_show_on_toplevel_help_message() {
+ let cmd = Command::new("myapp")
+ .arg(
+ Arg::new("someglobal")
+ .short('g')
+ .long("some-global")
+ .global(true),
+ )
+ .subcommand(Command::new("subcmd")
+ .help_heading("First Heading")
+ .subcommand(Command::new("multi")
+ .help_heading("Second Heading")
+ .version("1.0")));
+
+ let expected = str![[r#"
+Usage: myapp [OPTIONS] [COMMAND]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+First Heading:
+ subcmd
+
+Options:
+ -g, --some-global
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "myapp help", expected, false);
+}
+
+#[test]
+fn global_args_should_not_show_on_help_message_for_help_help() {
+ let cmd = Command::new("myapp")
+ .arg(
+ Arg::new("someglobal")
+ .short('g')
+ .long("some-global")
+ .global(true),
+ )
+ .subcommand(Command::new("subcmd")
+ .help_heading("First Heading")
+ .subcommand(Command::new("multi")
+ .help_heading("Second Heading")
+ .version("1.0")));
+
+ let expected = str![[r#"
+Print this message or the help of the given subcommand(s)
+
+Usage: myapp help [COMMAND]...
+
+Arguments:
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "myapp help help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+fn global_args_should_show_on_help_message_for_subcommand() {
+ let cmd = Command::new("myapp")
+ .arg(
+ Arg::new("someglobal")
+ .short('g')
+ .long("some-global")
+ .global(true),
+ )
+ .subcommand(Command::new("subcmd")
+ .help_heading("First Heading")
+ .subcommand(Command::new("multi")
+ .help_heading("Second Heading")
+ .version("1.0")));
+
+ let expected = str![[r#"
+Usage: myapp subcmd [OPTIONS] [COMMAND]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Second Heading:
+ multi
+
+Options:
+ -g, --some-global
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "myapp help subcmd", expected, false);
+}
+
+#[test]
+fn global_args_should_show_on_help_message_for_nested_subcommand() {
+ let cmd = Command::new("myapp")
+ .arg(
+ Arg::new("someglobal")
+ .short('g')
+ .long("some-global")
+ .global(true),
+ )
+ .subcommand(Command::new("subcmd")
+ .help_heading("First Heading")
+ .subcommand(Command::new("multi")
+ .help_heading("Second Heading")
+ .version("1.0")));
+
+ let expected = str![[r#"
+Usage: myapp subcmd multi [OPTIONS]
+
+Options:
+ -g, --some-global
+ -h, --help Print help
+ -V, --version Print version
+
+"#]];
+ utils::assert_output(cmd, "myapp help subcmd multi", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+fn prefer_about_over_long_about_in_subcommands_list() {
+ let cmd = Command::new("about-in-subcommands-list").subcommand(
+ Command::new("sub")
+ .help_heading("Custom Heading")
+ .long_about("long about sub")
+ .about("short about sub"),
+ );
+
+ let expected = str![[r#"
+Usage: about-in-subcommands-list [COMMAND]
+
+Commands:
+ help Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ sub short about sub
+
+Options:
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "about-in-subcommands-list --help", expected, false);
+}
+
+#[test]
+fn disabled_help_flag() {
+ let res = Command::new("foo")
+ .subcommand(Command::new("sub")
+ .help_heading("Custom Heading"))
+ .disable_help_flag(true)
+ .try_get_matches_from("foo a".split(' '));
+ assert!(res.is_err());
+ let err = res.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidSubcommand);
+}
+
+#[test]
+fn disabled_help_flag_and_subcommand() {
+ let res = Command::new("foo")
+ .subcommand(Command::new("sub")
+ .help_heading("Custom Heading"))
+ .disable_help_flag(true)
+ .disable_help_subcommand(true)
+ .try_get_matches_from("foo help".split(' '));
+ assert!(res.is_err());
+ let err = res.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidSubcommand);
+ assert!(
+ err.to_string().ends_with('\n'),
+ "Errors should have a trailing newline, got {:?}",
+ err.to_string()
+ );
+}
+
+#[test]
+fn override_help_subcommand() {
+ let cmd = Command::new("bar")
+ .subcommand(Command::new("help")
+ .help_heading("Help Heading")
+ .arg(Arg::new("arg").action(ArgAction::Set)))
+ .subcommand(Command::new("not_help")
+ .help_heading("Not Help")
+ .arg(Arg::new("arg").action(ArgAction::Set)))
+ .disable_help_subcommand(true);
+ let matches = cmd.try_get_matches_from(["bar", "help", "foo"]).unwrap();
+ assert_eq!(
+ matches
+ .subcommand_matches("help")
+ .unwrap()
+ .get_one::("arg")
+ .map(|v| v.as_str()),
+ Some("foo")
+ );
+}
+
+#[test]
+fn override_help_flag_using_long() {
+ let cmd = Command::new("foo")
+ .subcommand(Command::new("help")
+ .help_heading("Custom Heading")
+ .long_flag("help"))
+ .disable_help_flag(true)
+ .disable_help_subcommand(true);
+ let matches = cmd.try_get_matches_from(["foo", "--help"]).unwrap();
+ assert!(matches.subcommand_matches("help").is_some());
+}
+
+#[test]
+fn override_help_flag_using_short() {
+ let cmd = Command::new("foo")
+ .disable_help_flag(true)
+ .disable_help_subcommand(true)
+ .subcommand(Command::new("help")
+ .help_heading("Custom Heading")
+ .short_flag('h'));
+ let matches = cmd.try_get_matches_from(["foo", "-h"]).unwrap();
+ assert!(matches.subcommand_matches("help").is_some());
+}
+
+#[test]
+fn subcommand_help_doesnt_have_useless_help_flag() {
+ // The main care-about is that the docs and behavior match. Since the `help` subcommand
+ // currently ignores the `--help` flag, the output shouldn't have it.
+ let cmd = Command::new("example")
+ .subcommand(Command::new("test")
+ .help_heading("Custom Heading")
+ .about("Subcommand"));
+
+ let expected = str![[r#"
+Print this message or the help of the given subcommand(s)
+
+Usage: example help [COMMAND]...
+
+Arguments:
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "example help help", expected, false);
+}
+
+#[test]
+fn disable_help_flag_affects_help_subcommand() {
+ let mut cmd = Command::new("test_app")
+ .disable_help_flag(true)
+ .subcommand(Command::new("test")
+ .help_heading("Custom Heading")
+ .about("Subcommand"));
+ cmd.build();
+
+ let args = cmd
+ .find_subcommand("help")
+ .unwrap()
+ .get_arguments()
+ .map(|a| a.get_id().as_str())
+ .collect::>();
+ assert!(
+ !args.contains(&"help"),
+ "`help` should not be present: {args:?}"
+ );
+}
+
+#[test]
+fn dont_propagate_version_to_help_subcommand() {
+ let cmd = Command::new("example")
+ .version("1.0")
+ .propagate_version(true)
+ .subcommand(Command::new("subcommand")
+ .help_heading("Custom Heading"));
+
+ let expected = str![[r#"
+Print this message or the help of the given subcommand(s)
+
+Usage: example help [COMMAND]...
+
+Arguments:
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd.clone(), "example help help", expected, false);
+
+ cmd.debug_assert();
+}
+
+
+#[test]
+fn parent_cmd_req_in_usage_with_help_flag() {
+ let cmd = Command::new("parent")
+ .version("0.1")
+ .arg(Arg::new("TARGET").required(true).help("some"))
+ .arg(
+ Arg::new("ARGS")
+ .action(ArgAction::Set)
+ .required(true)
+ .help("some"),
+ )
+ .subcommand(Command::new("test")
+ .help_heading("Custom Heading")
+ .about("some"));
+
+ let expected = str![[r#"
+some
+
+Usage: parent test
+
+Options:
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "parent test --help", expected, false);
+}
+
+#[test]
+fn parent_cmd_req_in_usage_with_help_subcommand() {
+ let cmd = Command::new("parent")
+ .version("0.1")
+ .arg(Arg::new("TARGET").required(true).help("some"))
+ .arg(
+ Arg::new("ARGS")
+ .action(ArgAction::Set)
+ .required(true)
+ .help("some"),
+ )
+ .subcommand(Command::new("test")
+ .help_heading("Custom Heading")
+ .about("some"));
+
+ let expected = str![[r#"
+some
+
+Usage: parent test
+
+Options:
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "parent help test", expected, false);
+}
+
+#[test]
+fn parent_cmd_req_in_usage_with_render_help() {
+ let mut cmd = Command::new("parent")
+ .version("0.1")
+ .arg(Arg::new("TARGET").required(true).help("some"))
+ .arg(
+ Arg::new("ARGS")
+ .action(ArgAction::Set)
+ .required(true)
+ .help("some"),
+ )
+ .subcommand(Command::new("test")
+ .help_heading("Custom Heading")
+ .about("some"));
+ cmd.build();
+ let subcmd = cmd.find_subcommand_mut("test").unwrap();
+
+ let help = subcmd.render_help().to_string();
+ assert_data_eq!(
+ help,
+ str![[r#"
+some
+
+Usage: parent test
+
+Options:
+ -h, --help Print help
+
+"#]]
+ );
+}
+
+#[test]
+fn parent_cmd_req_ignored_when_negates_reqs() {
+ let cmd = Command::new("ctest")
+ .arg(arg!( ))
+ .subcommand_negates_reqs(true)
+ .subcommand(Command::new("subcmd")
+ .help_heading("Custom Heading"));
+
+ let expected = str![[r#"
+Usage: ctest subcmd
+
+Options:
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "ctest subcmd --help", expected, false);
+}
+
+#[test]
+fn parent_cmd_req_ignored_when_conflicts() {
+ let cmd = Command::new("ctest")
+ .arg(arg!( ))
+ .args_conflicts_with_subcommands(true)
+ .subcommand(Command::new("subcmd")
+ .help_heading("Custom Heading"));
+
+ let expected = str![[r#"
+Usage: ctest subcmd
+
+Options:
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "ctest subcmd --help", expected, false);
+}
+
+#[test]
+fn display_name_subcommand_default() {
+ let mut cmd = Command::new("parent")
+ .subcommand(Command::new("child")
+ .help_heading("Custom Heading")
+ .bin_name("child.exe"));
+ cmd.build();
+ assert_eq!(
+ cmd.find_subcommand("child").unwrap().get_display_name(),
+ Some("parent-child")
+ );
+}
+
+#[test]
+fn display_name_subcommand_explicit() {
+ let mut cmd = Command::new("parent").subcommand(
+ Command::new("child")
+ .help_heading("Custom Heading")
+ .bin_name("child.exe")
+ .display_name("child.display"),
+ );
+ cmd.build();
+ assert_eq!(
+ cmd.find_subcommand("child").unwrap().get_display_name(),
+ Some("child.display")
+ );
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_basic() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(Arg::new("parent").long("parent"))
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("test command")
+ .arg(Arg::new("child").long("child")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent test [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent test:
+test command
+ --child
+ -h, --help Print help
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_short_help() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(
+ Arg::new("parent")
+ .long("parent")
+ .help("foo")
+ .long_help("bar"),
+ )
+ .subcommand(
+ Command::new("test")
+ .about("test command")
+ .help_heading("Custom Heading")
+ .long_about("long some")
+ .arg(Arg::new("child").long("child").help("foo").long_help("bar")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent test [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent foo
+ -h, --help Print help (see more with '--help')
+
+parent test:
+test command
+ --child foo
+ -h, --help Print help (see more with '--help')
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_long_help() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(
+ Arg::new("parent")
+ .long("parent")
+ .help("foo")
+ .long_help("bar"),
+ )
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("test command")
+ .long_about("long some")
+ .arg(Arg::new("child").long("child").help("foo").long_help("bar")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent test [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ bar
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+parent test:
+test command
+ --child
+ bar
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]...
+ Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent --help", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_help_cmd() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(
+ Arg::new("parent")
+ .long("parent")
+ .help("foo")
+ .long_help("bar"),
+ )
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("test command")
+ .long_about("long some")
+ .arg(Arg::new("child").long("child").help("foo").long_help("bar")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent test [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ bar
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+parent test:
+test command
+ --child
+ bar
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]...
+ Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent help", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_with_global() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(Arg::new("parent").long("parent").global(true))
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("test command")
+ .arg(Arg::new("child").long("child")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent test [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent test:
+test command
+ --child
+ -h, --help Print help
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_arg_required() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(Arg::new("parent").long("parent").required(true))
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("test command")
+ .arg(Arg::new("child").long("child").required(true)),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent --parent
+ parent --parent test --child
+ parent --parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent --parent test:
+test command
+ --child
+ -h, --help Print help
+
+parent --parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_with_external_subcommand() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .allow_external_subcommands(true)
+ .arg(Arg::new("parent").long("parent"))
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("test command")
+ .arg(Arg::new("child").long("child")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent test [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent test:
+test command
+ --child
+ -h, --help Print help
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_with_subcommand_required() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .subcommand_required(true)
+ .arg(Arg::new("parent").long("parent"))
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("test command")
+ .arg(Arg::new("child").long("child")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent test [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent test:
+test command
+ --child
+ -h, --help Print help
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_with_args_conflicts_with_subcommands() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .subcommand_required(true)
+ .args_conflicts_with_subcommands(true)
+ .arg(Arg::new("parent").long("parent"))
+ .subcommand(
+ Command::new("test")
+ .help_heading("Custom Heading")
+ .about("test command")
+ .arg(Arg::new("child").long("child")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent test [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent test:
+test command
+ --child
+ -h, --help Print help
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+#[test]
+fn flatten_single_hidden_command() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(Arg::new("parent").long("parent"))
+ .subcommand(
+ Command::new("child1")
+ .help_heading("Custom Heading")
+ .hide(true)
+ .about("child1 command")
+ .arg(Arg::new("child").long("child1")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+
+Options:
+ --parent
+ -h, --help Print help
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_hidden_command() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(Arg::new("parent").long("parent"))
+ .subcommand(
+ Command::new("child1")
+ .help_heading("Custom Heading")
+ .about("child1 command")
+ .arg(Arg::new("child").long("child1")),
+ )
+ .subcommand(
+ Command::new("child2")
+ .help_heading("Custom Heading")
+ .about("child2 command")
+ .arg(Arg::new("child").long("child2")),
+ )
+ .subcommand(
+ Command::new("child3")
+ .help_heading("Custom Heading")
+ .hide(true)
+ .about("child3 command")
+ .arg(Arg::new("child").long("child3")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent child1 [OPTIONS]
+ parent child2 [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent child1:
+child1 command
+ --child1
+ -h, --help Print help
+
+parent child2:
+child2 command
+ --child2
+ -h, --help Print help
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_recursive() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(Arg::new("parent").long("parent"))
+ .subcommand(
+ Command::new("child1")
+ .flatten_help(true)
+ .help_heading("Custom Heading")
+ .about("child1 command")
+ .arg(Arg::new("child").long("child1"))
+ .subcommand(
+ Command::new("grandchild1")
+ .flatten_help(true)
+ .help_heading("Custom Heading")
+ .about("grandchild1 command")
+ .arg(Arg::new("grandchild").long("grandchild1"))
+ .subcommand(
+ Command::new("greatgrandchild1")
+ .help_heading("Custom Heading")
+ .about("greatgrandchild1 command")
+ .arg(Arg::new("greatgrandchild").long("greatgrandchild1")),
+ )
+ .subcommand(
+ Command::new("greatgrandchild2")
+ .help_heading("Custom Heading")
+ .about("greatgrandchild2 command")
+ .arg(Arg::new("greatgrandchild").long("greatgrandchild2")),
+ )
+ .subcommand(
+ Command::new("greatgrandchild3")
+ .help_heading("Custom Heading")
+ .about("greatgrandchild3 command")
+ .arg(Arg::new("greatgrandchild").long("greatgrandchild3")),
+ ),
+ )
+ .subcommand(
+ Command::new("grandchild2")
+ .help_heading("Custom Heading")
+ .about("grandchild2 command")
+ .arg(Arg::new("grandchild").long("grandchild2")),
+ )
+ .subcommand(
+ Command::new("grandchild3")
+ .help_heading("Custom Heading")
+ .about("grandchild3 command")
+ .arg(Arg::new("grandchild").long("grandchild3")),
+ ),
+ )
+ .subcommand(
+ Command::new("child2")
+ .help_heading("Custom Heading")
+ .about("child2 command")
+ .arg(Arg::new("child").long("child2")),
+ )
+ .subcommand(
+ Command::new("child3")
+ .hide(true)
+ .help_heading("Custom Heading")
+ .about("child3 command")
+ .arg(Arg::new("child").long("child3"))
+ .subcommand(
+ Command::new("grandchild1")
+ .flatten_help(true)
+ .help_heading("Custom Heading")
+ .about("grandchild1 command")
+ .arg(Arg::new("grandchild").long("grandchild1"))
+ .subcommand(
+ Command::new("greatgrandchild1")
+ .help_heading("Custom Heading")
+ .about("greatgrandchild1 command")
+ .arg(Arg::new("greatgrandchild").long("greatgrandchild1")),
+ )
+ .subcommand(
+ Command::new("greatgrandchild2")
+ .help_heading("Custom Heading")
+ .about("greatgrandchild2 command")
+ .arg(Arg::new("greatgrandchild").long("greatgrandchild2")),
+ )
+ .subcommand(
+ Command::new("greatgrandchild3")
+ .help_heading("Custom Heading")
+ .about("greatgrandchild3 command")
+ .arg(Arg::new("greatgrandchild").long("greatgrandchild3")),
+ ),
+ )
+ .subcommand(
+ Command::new("grandchild2")
+ .help_heading("Custom Heading")
+ .about("grandchild2 command")
+ .arg(Arg::new("grandchild").long("grandchild2")),
+ )
+ .subcommand(
+ Command::new("grandchild3")
+ .help_heading("Custom Heading")
+ .about("grandchild3 command")
+ .arg(Arg::new("grandchild").long("grandchild3")),
+ ),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent child1 [OPTIONS]
+ parent child1 grandchild1 [OPTIONS]
+ parent child1 grandchild1 greatgrandchild1 [OPTIONS]
+ parent child1 grandchild1 greatgrandchild2 [OPTIONS]
+ parent child1 grandchild1 greatgrandchild3 [OPTIONS]
+ parent child1 grandchild1 help [COMMAND]
+ parent child1 grandchild2 [OPTIONS]
+ parent child1 grandchild3 [OPTIONS]
+ parent child1 help [COMMAND]
+ parent child2 [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent child1:
+child1 command
+ --child1
+ -h, --help Print help
+
+parent child1 grandchild1:
+grandchild1 command
+ --grandchild1
+ -h, --help Print help
+
+parent child1 grandchild1 greatgrandchild1:
+greatgrandchild1 command
+ --greatgrandchild1
+ -h, --help Print help
+
+parent child1 grandchild1 greatgrandchild2:
+greatgrandchild2 command
+ --greatgrandchild2
+ -h, --help Print help
+
+parent child1 grandchild1 greatgrandchild3:
+greatgrandchild3 command
+ --greatgrandchild3
+ -h, --help Print help
+
+parent child1 grandchild1 help:
+Print this message or the help of the given subcommand(s)
+
+parent child1 grandchild2:
+grandchild2 command
+ --grandchild2
+ -h, --help Print help
+
+parent child1 grandchild3:
+grandchild3 command
+ --grandchild3
+ -h, --help Print help
+
+parent child1 help:
+Print this message or the help of the given subcommand(s)
+
+parent child2:
+child2 command
+ --child2
+ -h, --help Print help
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Parent Help - should it be the first (as normally under Command
+// and not custom heading) or should it be the last as is expected here?
+#[test]
+fn flatten_not_recursive() {
+ let cmd = Command::new("parent")
+ .flatten_help(true)
+ .about("parent command")
+ .arg(Arg::new("parent").long("parent"))
+ .subcommand(
+ Command::new("child1")
+ .help_heading("Custom Heading")
+ .about("child1 command")
+ .arg(Arg::new("child").long("child1"))
+ .subcommand(
+ Command::new("grandchild1")
+ .help_heading("Custom Heading")
+ .about("grandchild1 command")
+ .arg(Arg::new("grandchild").long("grandchild1")),
+ )
+ .subcommand(
+ Command::new("grandchild2")
+ .help_heading("Custom Heading")
+ .about("grandchild2 command")
+ .arg(Arg::new("grandchild").long("grandchild2")),
+ )
+ .subcommand(
+ Command::new("grandchild3")
+ .help_heading("Custom Heading")
+ .about("grandchild3 command")
+ .arg(Arg::new("grandchild").long("grandchild3")),
+ ),
+ )
+ .subcommand(
+ Command::new("child2")
+ .help_heading("Custom Heading")
+ .about("child2 command")
+ .arg(Arg::new("child").long("child2")),
+ )
+ .subcommand(
+ Command::new("child3")
+ .help_heading("Custom Heading")
+ .about("child3 command")
+ .arg(Arg::new("child").long("child3")),
+ );
+
+ let expected = str![[r#"
+parent command
+
+Usage: parent [OPTIONS]
+ parent child1 [OPTIONS] [COMMAND]
+ parent child2 [OPTIONS]
+ parent child3 [OPTIONS]
+ parent help [COMMAND]...
+
+Options:
+ --parent
+ -h, --help Print help
+
+parent child1:
+child1 command
+ --child1
+ -h, --help Print help
+
+parent child2:
+child2 command
+ --child2
+ -h, --help Print help
+
+parent child3:
+child3 command
+ --child3
+ -h, --help Print help
+
+parent help:
+Print this message or the help of the given subcommand(s)
+ [COMMAND]... Print help for the subcommand(s)
+
+"#]];
+ utils::assert_output(cmd, "parent -h", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+#[cfg(feature = "wrap_help")]
+fn next_line_command_short() {
+ let value_name = "V";
+ let text = "Hello";
+
+ let cmd = Command::new("test")
+ .term_width(120)
+ .next_line_help(true)
+ .args([
+ Arg::new("default")
+ .long("default")
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ Arg::new("next_line_help_false")
+ .long("next_line_help_false")
+ .next_line_help(false)
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ Arg::new("next_line_help_true")
+ .long("next_line_help_true")
+ .next_line_help(true)
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ ])
+ .subcommands([
+ Command::new("default").about(text).long_about(text),
+ Command::new("next_line_help_false")
+ .help_heading("Custom Heading")
+ .next_line_help(false)
+ .about(text)
+ .long_about(text),
+ Command::new("next_line_help_true")
+ .help_heading("Custom Heading")
+ .next_line_help(true)
+ .about(text)
+ .long_about(text),
+ ]);
+
+ let expected = str![[r#"
+Usage: myprog [OPTIONS] [COMMAND]
+
+Commands:
+ default
+ Hello
+ help
+ Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ next_line_help_false
+ Hello
+ next_line_help_true
+ Hello
+
+Options:
+ --default
+ Hello
+ --next_line_help_false
+ Hello
+ --next_line_help_true
+ Hello
+ -h, --help
+ Print help (see more with '--help')
+
+"#]];
+ utils::assert_output(cmd.clone(), "myprog -h", expected, false);
+
+ let expected = str![[r#"
+Usage: myprog [OPTIONS] [COMMAND]
+
+Commands:
+ default
+ Hello
+ help
+ Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ next_line_help_false
+ Hello
+ next_line_help_true
+ Hello
+
+Options:
+ --default
+ Hello
+
+ --next_line_help_false
+ Hello
+
+ --next_line_help_true
+ Hello
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+"#]];
+ utils::assert_output(cmd, "myprog --help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+#[cfg(feature = "wrap_help")]
+fn next_line_arg_short() {
+ let value_name = "V";
+ let text = "Hello";
+
+ let cmd = Command::new("test")
+ .term_width(120)
+ .next_line_help(true)
+ .args([
+ Arg::new("default")
+ .long("default")
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ Arg::new("next_line_help_false")
+ .long("next_line_help_false")
+ .next_line_help(false)
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ Arg::new("next_line_help_true")
+ .long("next_line_help_true")
+ .next_line_help(true)
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ ])
+ .subcommands([
+ Command::new("default").about(text).long_about(text),
+ Command::new("next_line_help_false")
+ .help_heading("Custom Heading")
+ .next_line_help(false)
+ .about(text)
+ .long_about(text),
+ Command::new("next_line_help_true")
+ .help_heading("Custom Heading")
+ .next_line_help(true)
+ .about(text)
+ .long_about(text),
+ ]);
+
+ let expected = str![[r#"
+Usage: myprog [OPTIONS] [COMMAND]
+
+Commands:
+ default
+ Hello
+ help
+ Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ next_line_help_false
+ Hello
+ next_line_help_true
+ Hello
+
+Options:
+ --default
+ Hello
+ --next_line_help_false
+ Hello
+ --next_line_help_true
+ Hello
+ -h, --help
+ Print help (see more with '--help')
+
+"#]];
+ utils::assert_output(cmd.clone(), "myprog -h", expected, false);
+
+ let expected = str![[r#"
+Usage: myprog [OPTIONS] [COMMAND]
+
+Commands:
+ default
+ Hello
+ help
+ Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ next_line_help_false
+ Hello
+ next_line_help_true
+ Hello
+
+Options:
+ --default
+ Hello
+
+ --next_line_help_false
+ Hello
+
+ --next_line_help_true
+ Hello
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+"#]];
+ utils::assert_output(cmd, "myprog --help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+#[cfg(feature = "wrap_help")]
+fn next_line_command_wrapped() {
+ let value_name = "SOME_LONG_VALUE";
+ let text = "Also do versioning for private crates (will not be published)
+
+Specify inter dependency version numbers exactly with `=`
+
+Do not commit version changes
+
+Do not push generated commit and tags to git remote
+";
+
+ let cmd = Command::new("test")
+ .term_width(67)
+ .next_line_help(true)
+ .args([
+ Arg::new("default")
+ .long("default")
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ Arg::new("next_line_help_false")
+ .long("next_line_help_false")
+ .next_line_help(false)
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ Arg::new("next_line_help_true")
+ .long("next_line_help_true")
+ .next_line_help(true)
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ ])
+ .subcommands([
+ Command::new("default").about(text).long_about(text),
+ Command::new("next_line_help_false")
+ .help_heading("Custom Heading")
+ .next_line_help(false)
+ .about(text)
+ .long_about(text),
+ Command::new("next_line_help_true")
+ .help_heading("Custom Heading")
+ .next_line_help(true)
+ .about(text)
+ .long_about(text),
+ ]);
+
+ let expected = str![[r#"
+Usage: myprog [OPTIONS] [COMMAND]
+
+Commands:
+ default
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ help
+ Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ next_line_help_false
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ next_line_help_true
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+Options:
+ --default
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ --next_line_help_false
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ --next_line_help_true
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ -h, --help
+ Print help (see more with '--help')
+
+"#]];
+ utils::assert_output(cmd.clone(), "myprog -h", expected, false);
+
+ let expected = str![[r#"
+Usage: myprog [OPTIONS] [COMMAND]
+
+Commands:
+ default
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ help
+ Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ next_line_help_false
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ next_line_help_true
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+Options:
+ --default
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+ --next_line_help_false
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+ --next_line_help_true
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+"#]];
+ utils::assert_output(cmd, "myprog --help", expected, false);
+}
+
+// Updated to include the custom heading in the expected output
+#[test]
+#[cfg(feature = "wrap_help")]
+fn next_line_arg_wrapped() {
+ let value_name = "SOME_LONG_VALUE";
+ let text = "Also do versioning for private crates (will not be published)
+
+Specify inter dependency version numbers exactly with `=`
+
+Do not commit version changes
+
+Do not push generated commit and tags to git remote
+";
+
+ let cmd = Command::new("test")
+ .term_width(67)
+ .next_line_help(true)
+ .args([
+ Arg::new("default")
+ .long("default")
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ Arg::new("next_line_help_false")
+ .long("next_line_help_false")
+ .next_line_help(false)
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ Arg::new("next_line_help_true")
+ .long("next_line_help_true")
+ .next_line_help(true)
+ .value_name(value_name)
+ .help(text)
+ .long_help(text),
+ ])
+ .subcommands([
+ Command::new("default").about(text).long_about(text),
+ Command::new("next_line_help_false")
+ .help_heading("Custom Heading")
+ .next_line_help(false)
+ .about(text)
+ .long_about(text),
+ Command::new("next_line_help_true")
+ .help_heading("Custom Heading")
+ .next_line_help(true)
+ .about(text)
+ .long_about(text),
+ ]);
+
+ let expected = str![[r#"
+Usage: myprog [OPTIONS] [COMMAND]
+
+Commands:
+ default
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ help
+ Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ next_line_help_false
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ next_line_help_true
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+Options:
+ --default
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ --next_line_help_false
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ --next_line_help_true
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ -h, --help
+ Print help (see more with '--help')
+
+"#]];
+ utils::assert_output(cmd.clone(), "myprog -h", expected, false);
+
+ let expected = str![[r#"
+Usage: myprog [OPTIONS] [COMMAND]
+
+Commands:
+ default
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ help
+ Print this message or the help of the given subcommand(s)
+
+Custom Heading:
+ next_line_help_false
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+ next_line_help_true
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+Options:
+ --default
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+ --next_line_help_false
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+ --next_line_help_true
+ Also do versioning for private crates (will not be
+ published)
+
+ Specify inter dependency version numbers exactly with `=`
+
+ Do not commit version changes
+
+ Do not push generated commit and tags to git remote
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+"#]];
+ utils::assert_output(cmd, "myprog --help", expected, false);
+}
+
+}
\ No newline at end of file
diff --git a/tests/builder/utils.rs b/tests/builder/utils.rs
index 05d95b4e51c..2e5ced5ae05 100644
--- a/tests/builder/utils.rs
+++ b/tests/builder/utils.rs
@@ -114,3 +114,61 @@ pub(crate) fn complex_app() -> Command {
.arg(arg!([scpositional] "tests positionals")),
)
}
+
+pub(crate) fn complex_app_with_help_heading() -> Command {
+ let opt3_vals = ["fast", "slow"];
+ let pos3_vals = ["vi", "emacs"];
+
+ Command::new("clap-test")
+ .version("v1.4.8")
+ .about("tests clap library")
+ .author("Kevin K. ")
+ .help_template(FULL_TEMPLATE)
+ .arg(
+ arg!(
+ -o --option "tests options"
+ )
+ .required(false)
+ .num_args(1..)
+ .action(ArgAction::Append),
+ )
+ .arg(arg!([positional] "tests positionals"))
+ .arg(
+ arg!(-f --flag "tests flags")
+ .action(ArgAction::Count)
+ .global(true),
+ )
+ .args([
+ arg!(flag2: -F "tests flags with exclusions")
+ .conflicts_with("flag")
+ .requires("long-option-2")
+ .action(ArgAction::SetTrue),
+ arg!(--"long-option-2" "tests long options with exclusions")
+ .conflicts_with("option")
+ .requires("positional2"),
+ arg!([positional2] "tests positionals with exclusions"),
+ arg!(-O --option3 "specific vals").value_parser(opt3_vals),
+ arg!([positional3] ... "tests specific values").value_parser(pos3_vals),
+ arg!(--multvals "Tests multiple values, not mult occs")
+ .value_names(["one", "two"]),
+ arg!(--multvalsmo ... "Tests multiple values, and mult occs")
+ .value_names(["one", "two"]),
+ arg!(--minvals2 "Tests 2 min vals").num_args(2..),
+ arg!(--maxvals3 "Tests 3 max vals").num_args(1..=3),
+ arg!(--optvaleq "Tests optional value, require = sign")
+ .num_args(0..=1)
+ .require_equals(true),
+ arg!(--optvalnoeq "Tests optional value").num_args(0..=1),
+ ])
+ .subcommand(
+ Command::new("subcmd")
+ .about("tests subcommands")
+ .help_heading("Custom Heading")
+ .version("0.1")
+ .author("Kevin K. ")
+ .help_template(FULL_TEMPLATE)
+ .arg(arg!(-o --option "tests options").num_args(1..))
+ .arg(arg!(-s --subcmdarg "tests other args"))
+ .arg(arg!([scpositional] "tests positionals")),
+ )
+}
\ No newline at end of file
diff --git a/tests/derive/value_enum.rs b/tests/derive/value_enum.rs
index e0ed09bc161..3024efd0481 100644
--- a/tests/derive/value_enum.rs
+++ b/tests/derive/value_enum.rs
@@ -41,16 +41,14 @@ fn basic() {
#[test]
fn default_value() {
#[derive(clap::ValueEnum, PartialEq, Debug, Clone)]
+ #[derive(Default)]
enum ArgChoice {
Foo,
+ #[default]
Bar,
}
- impl Default for ArgChoice {
- fn default() -> Self {
- Self::Bar
- }
- }
+
#[derive(Parser, PartialEq, Debug)]
struct Opt {
diff --git a/tests/derive_ui.rs b/tests/derive_ui.rs
index 84abd3e5304..2df86284186 100644
--- a/tests/derive_ui.rs
+++ b/tests/derive_ui.rs
@@ -8,7 +8,7 @@
#![cfg(feature = "unstable-derive-ui-tests")]
#[cfg(feature = "derive")]
-#[rustversion::attr(not(stable(1.90)), ignore)] // STABLE
+#[rustversion::attr(not(stable(1.91)), ignore)] // STABLE
#[test]
fn ui() {
let t = trybuild::TestCases::new();
diff --git a/tests/derive_ui/enum_variant_not_args.stderr b/tests/derive_ui/enum_variant_not_args.stderr
index 32ea375254a..2b9ea252c80 100644
--- a/tests/derive_ui/enum_variant_not_args.stderr
+++ b/tests/derive_ui/enum_variant_not_args.stderr
@@ -2,8 +2,13 @@ error[E0277]: the trait bound `SubCmd: clap::Args` is not satisfied
--> tests/derive_ui/enum_variant_not_args.rs:3:9
|
3 | Sub(SubCmd),
- | ^^^^^^ the trait `clap::Args` is not implemented for `SubCmd`
+ | ^^^^^^ unsatisfied trait bound
|
+help: the trait `clap::Args` is not implemented for `SubCmd`
+ --> tests/derive_ui/enum_variant_not_args.rs:7:1
+ |
+7 | enum SubCmd {}
+ | ^^^^^^^^^^^
= help: the following other types implement trait `clap::Args`:
()
Box
diff --git a/tests/derive_ui/flatten_enum_in_struct.stderr b/tests/derive_ui/flatten_enum_in_struct.stderr
index 4ad37382ee8..e21deedcd72 100644
--- a/tests/derive_ui/flatten_enum_in_struct.stderr
+++ b/tests/derive_ui/flatten_enum_in_struct.stderr
@@ -2,8 +2,13 @@ error[E0277]: the trait bound `SubCmd: clap::Args` is not satisfied
--> tests/derive_ui/flatten_enum_in_struct.rs:4:10
|
4 | sub: SubCmd,
- | ^^^^^^ the trait `clap::Args` is not implemented for `SubCmd`
+ | ^^^^^^ unsatisfied trait bound
|
+help: the trait `clap::Args` is not implemented for `SubCmd`
+ --> tests/derive_ui/flatten_enum_in_struct.rs:8:1
+ |
+8 | enum SubCmd {}
+ | ^^^^^^^^^^^
= help: the following other types implement trait `clap::Args`:
()
Box
diff --git a/tests/derive_ui/flatten_struct_in_enum.stderr b/tests/derive_ui/flatten_struct_in_enum.stderr
index ae36513ef31..5ff7d3bb039 100644
--- a/tests/derive_ui/flatten_struct_in_enum.stderr
+++ b/tests/derive_ui/flatten_struct_in_enum.stderr
@@ -2,8 +2,13 @@ error[E0277]: the trait bound `SubCmd: Subcommand` is not satisfied
--> tests/derive_ui/flatten_struct_in_enum.rs:4:9
|
4 | Sub(SubCmd),
- | ^^^^^^ the trait `Subcommand` is not implemented for `SubCmd`
+ | ^^^^^^ unsatisfied trait bound
|
+help: the trait `Subcommand` is not implemented for `SubCmd`
+ --> tests/derive_ui/flatten_struct_in_enum.rs:8:1
+ |
+8 | struct SubCmd {}
+ | ^^^^^^^^^^^^^
= help: the following other types implement trait `Subcommand`:
()
Box
diff --git a/tests/derive_ui/skip_without_default.stderr b/tests/derive_ui/skip_without_default.stderr
index bddc122089d..1b0d684fac8 100644
--- a/tests/derive_ui/skip_without_default.stderr
+++ b/tests/derive_ui/skip_without_default.stderr
@@ -2,4 +2,10 @@ error[E0277]: the trait bound `Kind: Default` is not satisfied
--> tests/derive_ui/skip_without_default.rs:22:11
|
22 | #[arg(skip)]
- | ^^^^ the trait `Default` is not implemented for `Kind`
+ | ^^^^ unsatisfied trait bound
+ |
+help: the trait `Default` is not implemented for `Kind`
+ --> tests/derive_ui/skip_without_default.rs:12:1
+ |
+12 | enum Kind {
+ | ^^^^^^^^^
diff --git a/tests/derive_ui/value_parser_unsupported.stderr b/tests/derive_ui/value_parser_unsupported.stderr
index 72b128f641f..951bebcb6a7 100644
--- a/tests/derive_ui/value_parser_unsupported.stderr
+++ b/tests/derive_ui/value_parser_unsupported.stderr
@@ -37,6 +37,7 @@ note: the traits `From`, `FromStr`, `ValueEnum`, and `ValueParserFactory` must
|
293 | pub trait ValueEnum: Sized + Clone {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
--> $RUST/core/src/convert/mod.rs
--> $RUST/core/src/str/traits.rs
= note: this error originates in the macro `clap::value_parser` (in Nightly builds, run with -Z macro-backtrace for more info)