1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 20:23:55 +00:00

feat: added usage message replacement feature (#786)

* fix: show newline before footer only if footer is set and not empty

* feat: added usage message replacement feature

* fix: tests corrected for new help message formatting
This commit is contained in:
polistern 2023-01-04 15:04:54 +00:00 committed by GitHub
parent 291c58789c
commit d3505540e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 2 deletions

View File

@ -1062,6 +1062,10 @@ option_groups. These are:
- `.prefix_command()`: Like `allow_extras`, but stop immediately on the first
unrecognized item. It is ideal for allowing your app or subcommand to be a
"prefix" to calling another app.
- `.usage(message)`: Replace text to appear at the start of the help string
after description.
- `.usage(std::string())`: Set a callback to generate a string that will appear
at the start of the help string after description.
- `.footer(message)`: Set text to appear at the bottom of the help string.
- `.footer(std::string())`: Set a callback to generate a string that will appear
at the end of the help string.
@ -1356,8 +1360,9 @@ multiple calls or using `|` operations with the transform.
Many of the defaults for subcommands and even options are inherited from their
creators. The inherited default values for subcommands are `allow_extras`,
`prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`,
`footer`,`immediate_callback` and maximum number of required subcommands. The
help flag existence, name, and description are inherited, as well.
`usage`, `footer`, `immediate_callback` and maximum number of required
subcommands. The help flag existence, name, and description are inherited, as
well.
Options have defaults for `group`, `required`, `multi_option_policy`,
`ignore_case`, `ignore_underscore`, `delimiter`, and `disable_flag_override`. To

View File

@ -150,6 +150,12 @@ class App {
/// @name Help
///@{
/// Usage to put after program/subcommand description in the help output INHERITABLE
std::string usage_{};
/// This is a function that generates a usage to put after program/subcommand description in help output
std::function<std::string()> usage_callback_{};
/// Footer to put after all options in the help output INHERITABLE
std::string footer_{};
@ -947,6 +953,16 @@ class App {
/// @name Help
///@{
/// Set usage.
App *usage(std::string usage_string) {
usage_ = std::move(usage_string);
return this;
}
/// Set usage.
App *usage(std::function<std::string()> usage_function) {
usage_callback_ = std::move(usage_function);
return this;
}
/// Set footer.
App *footer(std::string footer_string) {
footer_ = std::move(footer_string);
@ -1055,6 +1071,11 @@ class App {
/// Get the group of this subcommand
CLI11_NODISCARD const std::string &get_group() const { return group_; }
/// Generate and return the usage.
CLI11_NODISCARD std::string get_usage() const {
return (usage_callback_) ? usage_callback_() + '\n' + usage_ : usage_;
}
/// Generate and return the footer.
CLI11_NODISCARD std::string get_footer() const {
return (footer_callback_) ? footer_callback_() + '\n' + footer_ : footer_;

View File

@ -46,6 +46,7 @@ CLI11_INLINE App::App(std::string app_description, std::string app_name, App *pa
configurable_ = parent_->configurable_;
allow_windows_style_options_ = parent_->allow_windows_style_options_;
group_ = parent_->group_;
usage_ = parent_->usage_;
footer_ = parent_->footer_;
formatter_ = parent_->formatter_;
config_formatter_ = parent_->config_formatter_;

View File

@ -91,6 +91,11 @@ CLI11_INLINE std::string Formatter::make_description(const App *app) const {
}
CLI11_INLINE std::string Formatter::make_usage(const App *app, std::string name) const {
std::string usage = app->get_usage();
if(!usage.empty()) {
return usage + "\n";
}
std::stringstream out;
out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;

View File

@ -455,6 +455,7 @@ TEST_CASE_METHOD(TApp, "SubcommandDefaults", "[creation]") {
CHECK(!app.get_configurable());
CHECK(!app.get_validate_positionals());
CHECK(app.get_usage().empty());
CHECK(app.get_footer().empty());
CHECK("Subcommands" == app.get_group());
CHECK(0u == app.get_require_subcommand_min());
@ -474,6 +475,7 @@ TEST_CASE_METHOD(TApp, "SubcommandDefaults", "[creation]") {
app.fallthrough();
app.validate_positionals();
app.usage("ussy");
app.footer("footy");
app.group("Stuff");
app.require_subcommand(2, 3);
@ -494,6 +496,7 @@ TEST_CASE_METHOD(TApp, "SubcommandDefaults", "[creation]") {
CHECK(app2->get_fallthrough());
CHECK(app2->get_validate_positionals());
CHECK(app2->get_configurable());
CHECK("ussy" == app2->get_usage());
CHECK("footy" == app2->get_footer());
CHECK("Stuff" == app2->get_group());
CHECK(0u == app2->get_require_subcommand_min());

View File

@ -26,6 +26,43 @@ TEST_CASE("THelp: Basic", "[help]") {
CHECK_THAT(help, Contains("Usage:"));
}
TEST_CASE("THelp: Usage", "[help]") {
CLI::App app{"My prog"};
app.usage("use: just use it");
std::string help = app.help();
CHECK_THAT(help, Contains("My prog"));
CHECK_THAT(help, Contains("-h,--help"));
CHECK_THAT(help, Contains("Options:"));
CHECK_THAT(help, Contains("use: just use it"));
}
TEST_CASE("THelp: UsageCallback", "[help]") {
CLI::App app{"My prog"};
app.usage([]() { return "use: just use it"; });
std::string help = app.help();
CHECK_THAT(help, Contains("My prog"));
CHECK_THAT(help, Contains("-h,--help"));
CHECK_THAT(help, Contains("Options:"));
CHECK_THAT(help, Contains("use: just use it"));
}
TEST_CASE("THelp: UsageCallbackBoth", "[help]") {
CLI::App app{"My prog"};
app.usage([]() { return "use: just use it"; });
app.usage("like 1, 2, and 3");
std::string help = app.help();
CHECK_THAT(help, Contains("My prog"));
CHECK_THAT(help, Contains("-h,--help"));
CHECK_THAT(help, Contains("Options:"));
CHECK_THAT(help, Contains("use: just use it"));
CHECK_THAT(help, Contains("like 1, 2, and 3"));
}
TEST_CASE("THelp: Footer", "[help]") {
CLI::App app{"My prog"};
app.footer("Report bugs to bugs@example.com");