diff --git a/README.md b/README.md index f08094d8..eee85a10 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 979237f3..512cf9b3 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -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 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 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_; diff --git a/include/CLI/impl/App_inl.hpp b/include/CLI/impl/App_inl.hpp index 71afaa5c..1a8d2507 100644 --- a/include/CLI/impl/App_inl.hpp +++ b/include/CLI/impl/App_inl.hpp @@ -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_; diff --git a/include/CLI/impl/Formatter_inl.hpp b/include/CLI/impl/Formatter_inl.hpp index 541cd347..914774ee 100644 --- a/include/CLI/impl/Formatter_inl.hpp +++ b/include/CLI/impl/Formatter_inl.hpp @@ -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; diff --git a/tests/CreationTest.cpp b/tests/CreationTest.cpp index eaf36889..a51abd48 100644 --- a/tests/CreationTest.cpp +++ b/tests/CreationTest.cpp @@ -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()); diff --git a/tests/HelpTest.cpp b/tests/HelpTest.cpp index 996e12ca..7550959f 100644 --- a/tests/HelpTest.cpp +++ b/tests/HelpTest.cpp @@ -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");