1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 20:53:52 +00:00

Group merge (#1039)

This PR adds a mechanism to hide groups but have their options visible
as part of the parent.
This works for option group names starting with a '+'

for example 
```
 CLI::App app;

    bool flag = false;
    std::optional<bool> optional_flag = std::nullopt;

    app.add_option("--tester");
    auto *m1=app.add_option_group("+tester");

    m1->add_option("--flag", flag, "description");
    m1->add_option("--optional_flag", optional_flag, "description");

    CLI11_PARSE(app,argc, argv);
```
will produce help as 
```txt
Options:
  -h,--help                   Print this help message and exit
  --tester
  --flag BOOLEAN              description
  --optional_flag BOOLEAN     description
```

instead of 
```
Options:
  -h,--help                   Print this help message and exit
  --tester
[Option Group: tester]
  Options:
    --flag BOOLEAN              description
    --optional_flag BOOLEAN     description

```

Fixes issue #1034 and a few other past issues or questions

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Philip Top 2024-05-20 11:14:55 -07:00 committed by GitHub
parent ccd68011a4
commit 08d840bfbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 65 additions and 6 deletions

View File

@ -1,5 +1,10 @@
# Changelog # Changelog
## Unreleased
- add mechanism to allow option groups to be hidden and all options be
considered part of the parent for help display
## Version 2.4: Unicode and TOML support ## Version 2.4: Unicode and TOML support
This version adds Unicode support, support for TOML standard including multiline This version adds Unicode support, support for TOML standard including multiline

View File

@ -1205,7 +1205,17 @@ auto hidden_group=app.add_option_group("");
``` ```
will create a group such that no options in that group are displayed in the help will create a group such that no options in that group are displayed in the help
string. string. For the purposes of help display, if the option group name starts with a
'+' it is treated as if it were not in a group for help and get_options. For
example:
```cpp
auto added_group=app.add_option_group("+sub");
```
In this case the help output will not reference the option group and options
inside of it will be treated for most purposes as if they were part of the
parent.
### Configuration file ### Configuration file

View File

@ -1340,6 +1340,11 @@ class Option_group : public App {
: App(std::move(group_description), "", parent) { : App(std::move(group_description), "", parent) {
group(group_name); group(group_name);
// option groups should have automatic fallthrough // option groups should have automatic fallthrough
if(group_name.empty() || group_name.front() == '+') {
// help will not be used by default in these contexts
set_help_flag("");
set_help_all_flag("");
}
} }
using App::add_option; using App::add_option;
/// Add an existing option to the Option_group /// Add an existing option to the Option_group

View File

@ -784,7 +784,14 @@ CLI11_INLINE std::vector<const Option *> App::get_options(const std::function<bo
[&filter](const Option *opt) { return !filter(opt); }), [&filter](const Option *opt) { return !filter(opt); }),
std::end(options)); std::end(options));
} }
for(const auto &subcp : subcommands_) {
// also check down into nameless subcommands
const App *subc = subcp.get();
if(subc->get_name().empty() && !subc->get_group().empty() && subc->get_group().front() == '+') {
std::vector<const Option *> subcopts = subc->get_options(filter);
options.insert(options.end(), subcopts.begin(), subcopts.end());
}
}
return options; return options;
} }
@ -798,7 +805,13 @@ CLI11_INLINE std::vector<Option *> App::get_options(const std::function<bool(Opt
std::remove_if(std::begin(options), std::end(options), [&filter](Option *opt) { return !filter(opt); }), std::remove_if(std::begin(options), std::end(options), [&filter](Option *opt) { return !filter(opt); }),
std::end(options)); std::end(options));
} }
for(auto &subc : subcommands_) {
// also check down into nameless subcommands
if(subc->get_name().empty() && !subc->get_group().empty() && subc->get_group().front() == '+') {
auto subcopts = subc->get_options(filter);
options.insert(options.end(), subcopts.begin(), subcopts.end());
}
}
return options; return options;
} }

View File

@ -180,11 +180,11 @@ CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMo
std::vector<std::string> subcmd_groups_seen; std::vector<std::string> subcmd_groups_seen;
for(const App *com : subcommands) { for(const App *com : subcommands) {
if(com->get_name().empty()) { if(com->get_name().empty()) {
if(!com->get_group().empty()) { if(com->get_group().empty() || com->get_group().front() == '+') {
out << make_expanded(com);
}
continue; continue;
} }
out << make_expanded(com);
}
std::string group_key = com->get_group(); std::string group_key = com->get_group();
if(!group_key.empty() && if(!group_key.empty() &&
std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) { std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {

View File

@ -221,6 +221,32 @@ TEST_CASE_METHOD(TApp, "BasicOptionGroupMin", "[optiongroup]") {
CHECK(std::string::npos != exactloc); CHECK(std::string::npos != exactloc);
} }
TEST_CASE_METHOD(TApp, "integratedOptionGroup", "[optiongroup]") {
auto *ogroup = app.add_option_group("+clusters");
int res{0};
ogroup->add_option("--test1", res);
ogroup->add_option("--test2", res);
ogroup->add_option("--test3", res);
int val2{0};
app.add_option("--option", val2);
ogroup->require_option();
args = {"--option", "9"};
CHECK_THROWS_AS(run(), CLI::RequiredError);
args = {"--test1", "5", "--test2", "4", "--test3=5"};
CHECK_NOTHROW(run());
auto options = app.get_options();
CHECK(options.size() == 5);
const CLI::App *capp = &app;
auto coptions = capp->get_options();
CHECK(coptions.size() == 5);
std::string help = app.help();
auto exactloc = help.find("clusters");
CHECK(std::string::npos == exactloc);
}
TEST_CASE_METHOD(TApp, "BasicOptionGroupExact2", "[optiongroup]") { TEST_CASE_METHOD(TApp, "BasicOptionGroupExact2", "[optiongroup]") {
auto *ogroup = app.add_option_group("clusters"); auto *ogroup = app.add_option_group("clusters");
int res{0}; int res{0};