Commit d3505540e7d7b5384ce1d20b46b49ca7910e3708
Committed by
GitHub
1 parent
291c5878
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
Showing
6 changed files
with
74 additions
and
2 deletions
README.md
| ... | ... | @@ -1062,6 +1062,10 @@ option_groups. These are: |
| 1062 | 1062 | - `.prefix_command()`: Like `allow_extras`, but stop immediately on the first |
| 1063 | 1063 | unrecognized item. It is ideal for allowing your app or subcommand to be a |
| 1064 | 1064 | "prefix" to calling another app. |
| 1065 | +- `.usage(message)`: Replace text to appear at the start of the help string | |
| 1066 | + after description. | |
| 1067 | +- `.usage(std::string())`: Set a callback to generate a string that will appear | |
| 1068 | + at the start of the help string after description. | |
| 1065 | 1069 | - `.footer(message)`: Set text to appear at the bottom of the help string. |
| 1066 | 1070 | - `.footer(std::string())`: Set a callback to generate a string that will appear |
| 1067 | 1071 | at the end of the help string. |
| ... | ... | @@ -1356,8 +1360,9 @@ multiple calls or using `|` operations with the transform. |
| 1356 | 1360 | Many of the defaults for subcommands and even options are inherited from their |
| 1357 | 1361 | creators. The inherited default values for subcommands are `allow_extras`, |
| 1358 | 1362 | `prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`, |
| 1359 | -`footer`,`immediate_callback` and maximum number of required subcommands. The | |
| 1360 | -help flag existence, name, and description are inherited, as well. | |
| 1363 | +`usage`, `footer`, `immediate_callback` and maximum number of required | |
| 1364 | +subcommands. The help flag existence, name, and description are inherited, as | |
| 1365 | +well. | |
| 1361 | 1366 | |
| 1362 | 1367 | Options have defaults for `group`, `required`, `multi_option_policy`, |
| 1363 | 1368 | `ignore_case`, `ignore_underscore`, `delimiter`, and `disable_flag_override`. To | ... | ... |
include/CLI/App.hpp
| ... | ... | @@ -150,6 +150,12 @@ class App { |
| 150 | 150 | /// @name Help |
| 151 | 151 | ///@{ |
| 152 | 152 | |
| 153 | + /// Usage to put after program/subcommand description in the help output INHERITABLE | |
| 154 | + std::string usage_{}; | |
| 155 | + | |
| 156 | + /// This is a function that generates a usage to put after program/subcommand description in help output | |
| 157 | + std::function<std::string()> usage_callback_{}; | |
| 158 | + | |
| 153 | 159 | /// Footer to put after all options in the help output INHERITABLE |
| 154 | 160 | std::string footer_{}; |
| 155 | 161 | |
| ... | ... | @@ -947,6 +953,16 @@ class App { |
| 947 | 953 | /// @name Help |
| 948 | 954 | ///@{ |
| 949 | 955 | |
| 956 | + /// Set usage. | |
| 957 | + App *usage(std::string usage_string) { | |
| 958 | + usage_ = std::move(usage_string); | |
| 959 | + return this; | |
| 960 | + } | |
| 961 | + /// Set usage. | |
| 962 | + App *usage(std::function<std::string()> usage_function) { | |
| 963 | + usage_callback_ = std::move(usage_function); | |
| 964 | + return this; | |
| 965 | + } | |
| 950 | 966 | /// Set footer. |
| 951 | 967 | App *footer(std::string footer_string) { |
| 952 | 968 | footer_ = std::move(footer_string); |
| ... | ... | @@ -1055,6 +1071,11 @@ class App { |
| 1055 | 1071 | /// Get the group of this subcommand |
| 1056 | 1072 | CLI11_NODISCARD const std::string &get_group() const { return group_; } |
| 1057 | 1073 | |
| 1074 | + /// Generate and return the usage. | |
| 1075 | + CLI11_NODISCARD std::string get_usage() const { | |
| 1076 | + return (usage_callback_) ? usage_callback_() + '\n' + usage_ : usage_; | |
| 1077 | + } | |
| 1078 | + | |
| 1058 | 1079 | /// Generate and return the footer. |
| 1059 | 1080 | CLI11_NODISCARD std::string get_footer() const { |
| 1060 | 1081 | return (footer_callback_) ? footer_callback_() + '\n' + footer_ : footer_; | ... | ... |
include/CLI/impl/App_inl.hpp
| ... | ... | @@ -46,6 +46,7 @@ CLI11_INLINE App::App(std::string app_description, std::string app_name, App *pa |
| 46 | 46 | configurable_ = parent_->configurable_; |
| 47 | 47 | allow_windows_style_options_ = parent_->allow_windows_style_options_; |
| 48 | 48 | group_ = parent_->group_; |
| 49 | + usage_ = parent_->usage_; | |
| 49 | 50 | footer_ = parent_->footer_; |
| 50 | 51 | formatter_ = parent_->formatter_; |
| 51 | 52 | config_formatter_ = parent_->config_formatter_; | ... | ... |
include/CLI/impl/Formatter_inl.hpp
| ... | ... | @@ -91,6 +91,11 @@ CLI11_INLINE std::string Formatter::make_description(const App *app) const { |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | 93 | CLI11_INLINE std::string Formatter::make_usage(const App *app, std::string name) const { |
| 94 | + std::string usage = app->get_usage(); | |
| 95 | + if(!usage.empty()) { | |
| 96 | + return usage + "\n"; | |
| 97 | + } | |
| 98 | + | |
| 94 | 99 | std::stringstream out; |
| 95 | 100 | |
| 96 | 101 | out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name; | ... | ... |
tests/CreationTest.cpp
| ... | ... | @@ -455,6 +455,7 @@ TEST_CASE_METHOD(TApp, "SubcommandDefaults", "[creation]") { |
| 455 | 455 | CHECK(!app.get_configurable()); |
| 456 | 456 | CHECK(!app.get_validate_positionals()); |
| 457 | 457 | |
| 458 | + CHECK(app.get_usage().empty()); | |
| 458 | 459 | CHECK(app.get_footer().empty()); |
| 459 | 460 | CHECK("Subcommands" == app.get_group()); |
| 460 | 461 | CHECK(0u == app.get_require_subcommand_min()); |
| ... | ... | @@ -474,6 +475,7 @@ TEST_CASE_METHOD(TApp, "SubcommandDefaults", "[creation]") { |
| 474 | 475 | |
| 475 | 476 | app.fallthrough(); |
| 476 | 477 | app.validate_positionals(); |
| 478 | + app.usage("ussy"); | |
| 477 | 479 | app.footer("footy"); |
| 478 | 480 | app.group("Stuff"); |
| 479 | 481 | app.require_subcommand(2, 3); |
| ... | ... | @@ -494,6 +496,7 @@ TEST_CASE_METHOD(TApp, "SubcommandDefaults", "[creation]") { |
| 494 | 496 | CHECK(app2->get_fallthrough()); |
| 495 | 497 | CHECK(app2->get_validate_positionals()); |
| 496 | 498 | CHECK(app2->get_configurable()); |
| 499 | + CHECK("ussy" == app2->get_usage()); | |
| 497 | 500 | CHECK("footy" == app2->get_footer()); |
| 498 | 501 | CHECK("Stuff" == app2->get_group()); |
| 499 | 502 | CHECK(0u == app2->get_require_subcommand_min()); | ... | ... |
tests/HelpTest.cpp
| ... | ... | @@ -26,6 +26,43 @@ TEST_CASE("THelp: Basic", "[help]") { |
| 26 | 26 | CHECK_THAT(help, Contains("Usage:")); |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | +TEST_CASE("THelp: Usage", "[help]") { | |
| 30 | + CLI::App app{"My prog"}; | |
| 31 | + app.usage("use: just use it"); | |
| 32 | + | |
| 33 | + std::string help = app.help(); | |
| 34 | + | |
| 35 | + CHECK_THAT(help, Contains("My prog")); | |
| 36 | + CHECK_THAT(help, Contains("-h,--help")); | |
| 37 | + CHECK_THAT(help, Contains("Options:")); | |
| 38 | + CHECK_THAT(help, Contains("use: just use it")); | |
| 39 | +} | |
| 40 | + | |
| 41 | +TEST_CASE("THelp: UsageCallback", "[help]") { | |
| 42 | + CLI::App app{"My prog"}; | |
| 43 | + app.usage([]() { return "use: just use it"; }); | |
| 44 | + | |
| 45 | + std::string help = app.help(); | |
| 46 | + | |
| 47 | + CHECK_THAT(help, Contains("My prog")); | |
| 48 | + CHECK_THAT(help, Contains("-h,--help")); | |
| 49 | + CHECK_THAT(help, Contains("Options:")); | |
| 50 | + CHECK_THAT(help, Contains("use: just use it")); | |
| 51 | +} | |
| 52 | + | |
| 53 | +TEST_CASE("THelp: UsageCallbackBoth", "[help]") { | |
| 54 | + CLI::App app{"My prog"}; | |
| 55 | + app.usage([]() { return "use: just use it"; }); | |
| 56 | + app.usage("like 1, 2, and 3"); | |
| 57 | + std::string help = app.help(); | |
| 58 | + | |
| 59 | + CHECK_THAT(help, Contains("My prog")); | |
| 60 | + CHECK_THAT(help, Contains("-h,--help")); | |
| 61 | + CHECK_THAT(help, Contains("Options:")); | |
| 62 | + CHECK_THAT(help, Contains("use: just use it")); | |
| 63 | + CHECK_THAT(help, Contains("like 1, 2, and 3")); | |
| 64 | +} | |
| 65 | + | |
| 29 | 66 | TEST_CASE("THelp: Footer", "[help]") { |
| 30 | 67 | CLI::App app{"My prog"}; |
| 31 | 68 | app.footer("Report bugs to bugs@example.com"); | ... | ... |