Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Command groups #1553 #5816

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions clap_builder/src/builder/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::builder::Str;
use crate::builder::StyledStr;
use crate::builder::Styles;
use crate::builder::{Arg, ArgGroup, ArgPredicate};
use crate::builder::{CommandGroup};
use crate::error::ErrorKind;
use crate::error::Result as ClapResult;
use crate::mkeymap::MKeyMap;
Expand Down Expand Up @@ -100,10 +101,13 @@ pub struct Command {
args: MKeyMap,
subcommands: Vec<Command>,
groups: Vec<ArgGroup>,
command_groups: Vec<CommandGroup>,
current_help_heading: Option<Str>,
current_subcommand_help_heading: Option<Str>,
current_disp_ord: Option<usize>,
subcommand_value_name: Option<Str>,
subcommand_heading: Option<Str>,
subcommand_help_heading: Option<Option<Str>>,
external_value_parser: Option<super::ValueParser>,
long_help_exists: bool,
deferred: Option<fn(Command) -> Command>,
Expand Down Expand Up @@ -340,6 +344,44 @@ impl Command {
self.groups.push(f(a));
self
}

/// Allows one to mutate an [`CommandGroup`] after it's been added to a [`Command`].
///
/// # Panics
///
/// If the argument is undefined
///
/// # Examples
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, CommandGroup, arg, ArgGroup};
///
/// Command::new("foo")
/// .command_group(CommandGroup::new("bar")
/// .help_heading("Bar commands")
/// .command("bar")
/// .mut_command_group("bar", |g| g.clear());
/// ```

#[must_use]
#[cfg_attr(debug_assertions, track_caller)]
pub fn mut_command_group<F>(mut self, cmd_id: impl AsRef<str>, f: F) -> Self
where
F: FnOnce(CommandGroup) -> CommandGroup,
{
let id = cmd_id.as_ref();
let index = self
.command_groups
.iter()
.position(|g| g.get_id() == id)
.unwrap_or_else(|| panic!("Command group `{id}` is undefined"));
let a = self.command_groups.remove(index);

self.command_groups.push(f(a));
self
}

/// Allows one to mutate a [`Command`] after it's been added as a subcommand.
///
/// This can be useful for modifying auto-generated arguments of nested subcommands with
Expand Down Expand Up @@ -425,6 +467,40 @@ impl Command {
self
}

/// Adds an [`CommandGroup`] to the application.
///
/// [`CommandGroup`]s are a family of related subcommands.
/// By placing them in a logical group, you can display help more clearly.
///
/// # Examples
///
/// The following example demonstrates using an [`CommandGroup`] to group related commands
/// when usage help is displayed.
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, CommandGroup};
/// Command::new("add-pizza-ingredient")
/// .subcommand(Command::new("pepperoni"))
/// .subcommand(Command::new("ham"))
/// .subcommand(Command::new("peppers"))
/// .subcommand(Command::new("onion"))
/// .command_group(CommandGroup::new("meats")
/// .help_heading("Meaty ingredients")
/// .commands(["pepperoni", "ham"]))
/// .command_group(CommandGroup::new("veggies")
/// .help_heading("Vegetables")
/// .commands(["peppers", "onion"]))
/// # ;
/// ```
#[inline]
#[must_use]
pub fn command_group(mut self, group: impl Into<CommandGroup>) -> Self {
self.command_groups.push(group.into());
self
}


/// Adds multiple [`ArgGroup`]s to the [`Command`] at once.
///
/// # Examples
Expand Down Expand Up @@ -456,6 +532,42 @@ impl Command {
self
}

/// Adds an [`CommandGroup`] to the application.
///
/// [`CommandGroup`]s are a family of related subcommands.
/// By placing them in a logical group, you can display help more clearly.
///
/// # Examples
///
/// The following example demonstrates using an [`CommandGroup`] to group related commands
/// when usage help is displayed.
///
/// ```rust
/// # use clap_builder as clap;
/// # use clap::{Command, CommandGroup};
/// Command::new("add-pizza-ingredient")
/// .subcommand(Command::new("pepperoni"))
/// .subcommand(Command::new("ham"))
/// .subcommand(Command::new("peppers"))
/// .subcommand(Command::new("onion"))
/// .command_groups([
/// CommandGroup::new("meats")
/// .help_heading("Meaty ingredients")
/// .commands(["pepperoni", "ham"]),
/// CommandGroup::new("veggies")
/// .help_heading("Vegetables")
/// .commands(["peppers", "onion"])
/// ])
/// # ;
/// ```
#[must_use]
pub fn command_groups(mut self, groups: impl IntoIterator<Item = impl Into<CommandGroup>>) -> Self {
for g in groups {
self = self.command_group(g.into());
}
self
}

/// Adds a subcommand to the list of valid possibilities.
///
/// Subcommands are effectively sub-[`Command`]s, because they can contain their own arguments,
Expand Down Expand Up @@ -490,6 +602,8 @@ impl Command {
subcmd.disp_ord.get_or_insert(current);
*current_disp_ord = current + 1;
}
subcmd.subcommand_help_heading
.get_or_insert_with(|| self.current_subcommand_help_heading.clone());
self.subcommands.push(subcmd);
self
}
Expand Down Expand Up @@ -2137,6 +2251,32 @@ impl Command {
self
}

/// Set the default section heading for future subcommands.
///
/// This will be used for any subcommand that hasn't had [`Command::subcommand_help_heading`] called.
///
/// This is useful if the default `Commands` heading is
/// not specific enough for one's use case.
///
/// [`Command::subcommand`]: Command::subcommand()
/// [`Command::subcommand_help_heading`]: crate::Command::subcommand_help_heading()
#[inline]
#[must_use]
pub fn next_subcommand_help_heading(mut self, heading: impl IntoResettable<Str>) -> Self {
self.current_subcommand_help_heading = heading.into_resettable().into_option();
self
}

/*/// Change the starting value for assigning future display orders for args.
///
/// This will be used for any arg that hasn't had [`Arg::display_order`] called.
#[inline]
#[must_use]
pub fn next_subcommand_display_order(mut self, disp_ord: impl IntoResettable<usize>) -> Self {
self.current_subocommand_disp_ord = disp_ord.into_resettable().into_option();
self
}*/

/// Change the starting value for assigning future display orders for args.
///
/// This will be used for any arg that hasn't had [`Arg::display_order`] called.
Expand Down Expand Up @@ -3527,6 +3667,14 @@ impl Command {
self.current_help_heading.as_deref()
}

/// Get the custom section heading specified via [`Command::next_subcommand_help_heading`].
///
/// [`Command::subcommand_help_heading`]: Command::subcommand_help_heading()
#[inline]
pub fn get_next_subcommand_help_heading(&self) -> Option<&str> {
self.current_subcommand_help_heading.as_deref()
}

/// Iterate through the *visible* aliases for this subcommand.
#[inline]
pub fn get_visible_aliases(&self) -> impl Iterator<Item = &str> + '_ {
Expand Down Expand Up @@ -3694,6 +3842,12 @@ impl Command {
self.groups.iter()
}

/// Iterate through the set of command groups.
#[inline]
pub fn get_command_groups(&self) -> impl Iterator<Item = &CommandGroup> {
self.command_groups.iter()
}

/// Iterate through the set of arguments.
#[inline]
pub fn get_arguments(&self) -> impl Iterator<Item = &Arg> {
Expand Down Expand Up @@ -4139,6 +4293,22 @@ impl Command {

self.args._build();

for c in self.subcommands.iter_mut() {
// Fill in the command_groups
for g in &c.command_groups {
if let Some(cg) = self.command_groups.iter_mut().find(|grp| grp.id == g.id) {
cg.commands.push(c.name.clone());
} else {
let mut cg = CommandGroup::new(g.get_id().clone());
cg.commands.push(c.name.clone());
self.command_groups.push(cg);
}
}
}




#[allow(deprecated)]
{
let highest_idx = self
Expand Down Expand Up @@ -4899,10 +5069,14 @@ impl Default for Command {
args: Default::default(),
subcommands: Default::default(),
groups: Default::default(),
command_groups: Default::default(),
current_help_heading: Default::default(),
current_subcommand_help_heading: Default::default(),
current_disp_ord: Some(0),
//current_subcommand_disp_ord: Some(0),
subcommand_value_name: Default::default(),
subcommand_heading: Default::default(),
subcommand_help_heading: Default::default(),
external_value_parser: Default::default(),
long_help_exists: false,
deferred: None,
Expand Down
Loading
Loading