Skip to content

Commit

Permalink
Group merge (#1039)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
phlptp and pre-commit-ci[bot] authored May 20, 2024
1 parent ccd6801 commit 08d840b
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 6 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# 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

This version adds Unicode support, support for TOML standard including multiline
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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

Expand Down
5 changes: 5 additions & 0 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,11 @@ class Option_group : public App {
: App(std::move(group_description), "", parent) {
group(group_name);
// 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;
/// Add an existing option to the Option_group
Expand Down
17 changes: 15 additions & 2 deletions include/CLI/impl/App_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,14 @@ CLI11_INLINE std::vector<const Option *> App::get_options(const std::function<bo
[&filter](const Option *opt) { return !filter(opt); }),
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;
}

Expand All @@ -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::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;
}

Expand Down
6 changes: 3 additions & 3 deletions include/CLI/impl/Formatter_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,10 @@ CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMo
std::vector<std::string> subcmd_groups_seen;
for(const App *com : subcommands) {
if(com->get_name().empty()) {
if(!com->get_group().empty()) {
out << make_expanded(com);
if(com->get_group().empty() || com->get_group().front() == '+') {
continue;
}
continue;
out << make_expanded(com);
}
std::string group_key = com->get_group();
if(!group_key.empty() &&
Expand Down
26 changes: 26 additions & 0 deletions tests/OptionGroupTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,32 @@ TEST_CASE_METHOD(TApp, "BasicOptionGroupMin", "[optiongroup]") {
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]") {
auto *ogroup = app.add_option_group("clusters");
int res{0};
Expand Down

0 comments on commit 08d840b

Please sign in to comment.