Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added usage message replacement feature #786

Merged
merged 4 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ class App {
/// @name Help
///@{

/// Usage to put after program/subcommand description in the help output INHERITABLE
std::string usage_{};
henryiii marked this conversation as resolved.
Show resolved Hide resolved

/// 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_{};

Expand Down Expand Up @@ -942,6 +948,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);
Expand Down Expand Up @@ -1050,6 +1066,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_;
Expand Down
1 change: 1 addition & 0 deletions include/CLI/impl/App_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
5 changes: 5 additions & 0 deletions include/CLI/impl/Formatter_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions tests/CreationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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);
Expand All @@ -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());
Expand Down
37 changes: 37 additions & 0 deletions tests/HelpTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected]");
Expand Down