Skip to content

Commit 08d840b

Browse files
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>
1 parent ccd6801 commit 08d840b

File tree

6 files changed

+65
-6
lines changed

6 files changed

+65
-6
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- add mechanism to allow option groups to be hidden and all options be
6+
considered part of the parent for help display
7+
38
## Version 2.4: Unicode and TOML support
49

510
This version adds Unicode support, support for TOML standard including multiline

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,17 @@ auto hidden_group=app.add_option_group("");
12051205
```
12061206

12071207
will create a group such that no options in that group are displayed in the help
1208-
string.
1208+
string. For the purposes of help display, if the option group name starts with a
1209+
'+' it is treated as if it were not in a group for help and get_options. For
1210+
example:
1211+
1212+
```cpp
1213+
auto added_group=app.add_option_group("+sub");
1214+
```
1215+
1216+
In this case the help output will not reference the option group and options
1217+
inside of it will be treated for most purposes as if they were part of the
1218+
parent.
12091219

12101220
### Configuration file
12111221

include/CLI/App.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,11 @@ class Option_group : public App {
13401340
: App(std::move(group_description), "", parent) {
13411341
group(group_name);
13421342
// option groups should have automatic fallthrough
1343+
if(group_name.empty() || group_name.front() == '+') {
1344+
// help will not be used by default in these contexts
1345+
set_help_flag("");
1346+
set_help_all_flag("");
1347+
}
13431348
}
13441349
using App::add_option;
13451350
/// Add an existing option to the Option_group

include/CLI/impl/App_inl.hpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,14 @@ CLI11_INLINE std::vector<const Option *> App::get_options(const std::function<bo
784784
[&filter](const Option *opt) { return !filter(opt); }),
785785
std::end(options));
786786
}
787-
787+
for(const auto &subcp : subcommands_) {
788+
// also check down into nameless subcommands
789+
const App *subc = subcp.get();
790+
if(subc->get_name().empty() && !subc->get_group().empty() && subc->get_group().front() == '+') {
791+
std::vector<const Option *> subcopts = subc->get_options(filter);
792+
options.insert(options.end(), subcopts.begin(), subcopts.end());
793+
}
794+
}
788795
return options;
789796
}
790797

@@ -798,7 +805,13 @@ CLI11_INLINE std::vector<Option *> App::get_options(const std::function<bool(Opt
798805
std::remove_if(std::begin(options), std::end(options), [&filter](Option *opt) { return !filter(opt); }),
799806
std::end(options));
800807
}
801-
808+
for(auto &subc : subcommands_) {
809+
// also check down into nameless subcommands
810+
if(subc->get_name().empty() && !subc->get_group().empty() && subc->get_group().front() == '+') {
811+
auto subcopts = subc->get_options(filter);
812+
options.insert(options.end(), subcopts.begin(), subcopts.end());
813+
}
814+
}
802815
return options;
803816
}
804817

include/CLI/impl/Formatter_inl.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,10 @@ CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMo
180180
std::vector<std::string> subcmd_groups_seen;
181181
for(const App *com : subcommands) {
182182
if(com->get_name().empty()) {
183-
if(!com->get_group().empty()) {
184-
out << make_expanded(com);
183+
if(com->get_group().empty() || com->get_group().front() == '+') {
184+
continue;
185185
}
186-
continue;
186+
out << make_expanded(com);
187187
}
188188
std::string group_key = com->get_group();
189189
if(!group_key.empty() &&

tests/OptionGroupTest.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,32 @@ TEST_CASE_METHOD(TApp, "BasicOptionGroupMin", "[optiongroup]") {
221221
CHECK(std::string::npos != exactloc);
222222
}
223223

224+
TEST_CASE_METHOD(TApp, "integratedOptionGroup", "[optiongroup]") {
225+
auto *ogroup = app.add_option_group("+clusters");
226+
int res{0};
227+
ogroup->add_option("--test1", res);
228+
ogroup->add_option("--test2", res);
229+
ogroup->add_option("--test3", res);
230+
int val2{0};
231+
app.add_option("--option", val2);
232+
ogroup->require_option();
233+
234+
args = {"--option", "9"};
235+
CHECK_THROWS_AS(run(), CLI::RequiredError);
236+
237+
args = {"--test1", "5", "--test2", "4", "--test3=5"};
238+
CHECK_NOTHROW(run());
239+
240+
auto options = app.get_options();
241+
CHECK(options.size() == 5);
242+
const CLI::App *capp = &app;
243+
auto coptions = capp->get_options();
244+
CHECK(coptions.size() == 5);
245+
std::string help = app.help();
246+
auto exactloc = help.find("clusters");
247+
CHECK(std::string::npos == exactloc);
248+
}
249+
224250
TEST_CASE_METHOD(TApp, "BasicOptionGroupExact2", "[optiongroup]") {
225251
auto *ogroup = app.add_option_group("clusters");
226252
int res{0};

0 commit comments

Comments
 (0)