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 - `.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 unrecognized item. It is ideal for allowing your app or subcommand to be a
"prefix" to calling another app. "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(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 - `.footer(std::string())`: Set a callback to generate a string that will appear
at the end of the help string. 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 Many of the defaults for subcommands and even options are inherited from their
creators. The inherited default values for subcommands are `allow_extras`, creators. The inherited default values for subcommands are `allow_extras`,
`prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`, `prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`,
`footer`,`immediate_callback` and maximum number of required subcommands. The `usage`, `footer`, `immediate_callback` and maximum number of required
help flag existence, name, and description are inherited, as well. subcommands. The help flag existence, name, and description are inherited, as
well.
Options have defaults for `group`, `required`, `multi_option_policy`, Options have defaults for `group`, `required`, `multi_option_policy`,
`ignore_case`, `ignore_underscore`, `delimiter`, and `disable_flag_override`. To `ignore_case`, `ignore_underscore`, `delimiter`, and `disable_flag_override`. To

View File

@ -150,6 +150,12 @@ class App {
/// @name Help /// @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 /// Footer to put after all options in the help output INHERITABLE
std::string footer_{}; std::string footer_{};
@ -947,6 +953,16 @@ class App {
/// @name Help /// @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. /// Set footer.
App *footer(std::string footer_string) { App *footer(std::string footer_string) {
footer_ = std::move(footer_string); footer_ = std::move(footer_string);
@ -1055,6 +1071,11 @@ class App {
/// Get the group of this subcommand /// Get the group of this subcommand
CLI11_NODISCARD const std::string &get_group() const { return group_; } 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. /// Generate and return the footer.
CLI11_NODISCARD std::string get_footer() const { CLI11_NODISCARD std::string get_footer() const {
return (footer_callback_) ? footer_callback_() + '\n' + footer_ : footer_; 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_; configurable_ = parent_->configurable_;
allow_windows_style_options_ = parent_->allow_windows_style_options_; allow_windows_style_options_ = parent_->allow_windows_style_options_;
group_ = parent_->group_; group_ = parent_->group_;
usage_ = parent_->usage_;
footer_ = parent_->footer_; footer_ = parent_->footer_;
formatter_ = parent_->formatter_; formatter_ = parent_->formatter_;
config_formatter_ = parent_->config_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 { 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; std::stringstream out;
out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name; 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_configurable());
CHECK(!app.get_validate_positionals()); CHECK(!app.get_validate_positionals());
CHECK(app.get_usage().empty());
CHECK(app.get_footer().empty()); CHECK(app.get_footer().empty());
CHECK("Subcommands" == app.get_group()); CHECK("Subcommands" == app.get_group());
CHECK(0u == app.get_require_subcommand_min()); CHECK(0u == app.get_require_subcommand_min());
@ -474,6 +475,7 @@ TEST_CASE_METHOD(TApp, "SubcommandDefaults", "[creation]") {
app.fallthrough(); app.fallthrough();
app.validate_positionals(); app.validate_positionals();
app.usage("ussy");
app.footer("footy"); app.footer("footy");
app.group("Stuff"); app.group("Stuff");
app.require_subcommand(2, 3); app.require_subcommand(2, 3);
@ -494,6 +496,7 @@ TEST_CASE_METHOD(TApp, "SubcommandDefaults", "[creation]") {
CHECK(app2->get_fallthrough()); CHECK(app2->get_fallthrough());
CHECK(app2->get_validate_positionals()); CHECK(app2->get_validate_positionals());
CHECK(app2->get_configurable()); CHECK(app2->get_configurable());
CHECK("ussy" == app2->get_usage());
CHECK("footy" == app2->get_footer()); CHECK("footy" == app2->get_footer());
CHECK("Stuff" == app2->get_group()); CHECK("Stuff" == app2->get_group());
CHECK(0u == app2->get_require_subcommand_min()); CHECK(0u == app2->get_require_subcommand_min());

View File

@ -26,6 +26,43 @@ TEST_CASE("THelp: Basic", "[help]") {
CHECK_THAT(help, Contains("Usage:")); 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]") { TEST_CASE("THelp: Footer", "[help]") {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
app.footer("Report bugs to bugs@example.com"); app.footer("Report bugs to bugs@example.com");