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

Allow to customize help flag. #43

Merged
merged 5 commits into from
Nov 20, 2017
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ The add commands return a pointer to an internally stored `Option`. If you set t
* `->check(CLI::NonexistentPath)`: Requires that the path does not exist.
* `->check(CLI::Range(min,max))`: Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.

These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `bool(std::string)`. If you want to change the default help option, it is available through `get_help_ptr`. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results.
These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `bool(std::string)`. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results.


On the command line, options can be given as:
Expand Down Expand Up @@ -244,7 +244,7 @@ arguments, use `.config_to_str(default_also=false)`, where `default_also` will a

## Subclassing

The App class was designed allow toolkits to subclass it, to provide default options and setup/teardown code. Subcommands remain an unsubclassed `App`, since those are not expected to need setup and teardown. The default `App` only adds a help flag, `-h,--help`, but provides an option to disable it in the constructor (and in `add_subcommand`). You can remove options if you have pointers to them using `.remove_option(opt)`. You can add a `pre_callback` override to customize the after parse
The App class was designed allow toolkits to subclass it, to provide default options and setup/teardown code. Subcommands remain an unsubclassed `App`, since those are not expected to need setup and teardown. The default `App` only adds a help flag, `-h,--help`, but provides an option to disable it in the constructor (and in `add_subcommand`), and can removed/replaced using `.set_help_flag(name, help_string)`. You can remove options if you have pointers to them using `.remove_option(opt)`. You can add a `pre_callback` override to customize the after parse
but before run behavior, while
still giving the user freedom to `set_callback` on the main app.

Expand Down
16 changes: 15 additions & 1 deletion include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class App {
App(std::string description_, bool help, detail::enabler) : description_(std::move(description_)) {

if(help)
help_ptr_ = add_flag("-h,--help", "Print this help message and exit");
set_help_flag("-h,--help", "Print this help message and exit");
}

public:
Expand Down Expand Up @@ -322,6 +322,20 @@ class App {
return opt;
}

/// Set a help flag, replaced the existing one if present
Option *set_help_flag(std::string name = "", std::string description = "") {
if(help_ptr_ != nullptr) {
remove_option(help_ptr_);
help_ptr_ = nullptr;
}

// Empty name will simply remove the help flag
if(!name.empty())
help_ptr_ = add_flag(name, description);

return help_ptr_;
}

/// Add option for flag
Option *add_flag(std::string name, std::string description = "") {
CLI::callback_t fun = [](CLI::results_t) { return true; };
Expand Down
70 changes: 70 additions & 0 deletions tests/HelpTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,76 @@ TEST(THelp, SetLower) {
EXPECT_THAT(help, HasSubstr("THREE"));
}

TEST(THelp, OnlyOneHelp) {
CLI::App app{"My prog"};

// It is not supported to have more than one help flag, last one wins
app.set_help_flag("--help", "No short name allowed");
app.set_help_flag("--yelp", "Alias for help");

std::vector<std::string> input{"--help"};
EXPECT_THROW(app.parse(input), CLI::ExtrasError);
}

TEST(THelp, RemoveHelp) {
CLI::App app{"My prog"};
app.set_help_flag();

std::string help = app.help();

EXPECT_THAT(help, HasSubstr("My prog"));
EXPECT_THAT(help, Not(HasSubstr("-h,--help")));
EXPECT_THAT(help, Not(HasSubstr("Options:")));
EXPECT_THAT(help, HasSubstr("Usage:"));

std::vector<std::string> input{"--help"};
try {
app.parse(input);
} catch(const CLI::ParseError &e) {
EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Extras), e.get_exit_code());
}
}

TEST(THelp, NoHelp) {
CLI::App app{"My prog", false};

std::string help = app.help();

EXPECT_THAT(help, HasSubstr("My prog"));
EXPECT_THAT(help, Not(HasSubstr("-h,--help")));
EXPECT_THAT(help, Not(HasSubstr("Options:")));
EXPECT_THAT(help, HasSubstr("Usage:"));

std::vector<std::string> input{"--help"};
try {
app.parse(input);
} catch(const CLI::ParseError &e) {
EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Extras), e.get_exit_code());
}
}

TEST(THelp, CustomHelp) {
CLI::App app{"My prog", false};

CLI::Option *help_option = app.set_help_flag("--yelp", "display help and exit");
EXPECT_EQ(app.get_help_ptr(), help_option);

std::string help = app.help();

EXPECT_THAT(help, HasSubstr("My prog"));
EXPECT_THAT(help, Not(HasSubstr("-h,--help")));
EXPECT_THAT(help, HasSubstr("--yelp"));
EXPECT_THAT(help, HasSubstr("Options:"));
EXPECT_THAT(help, HasSubstr("Usage:"));

std::vector<std::string> input{"--yelp"};
try {
app.parse(input);
} catch(const CLI::CallForHelp &e) {
EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Success), e.get_exit_code());
}
}

TEST(Exit, ErrorWithHelp) {
CLI::App app{"My prog"};

Expand Down