From 3b67be713206ff5318ede57c50864d6fc55f716b Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Mon, 20 Nov 2017 02:33:10 +0100 Subject: [PATCH 1/5] Allow to customize help flag. --- include/CLI/App.hpp | 7 +++++++ tests/HelpTest.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 1cce85340..bb6e39982 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -322,6 +322,13 @@ class App { return opt; } + Option *add_help_flag(std::string name, std::string description = "") { + if(help_ptr_) + throw IncorrectConstruction("Help flag already initialized"); + 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; }; diff --git a/tests/HelpTest.cpp b/tests/HelpTest.cpp index a1ad292d3..d999191f5 100644 --- a/tests/HelpTest.cpp +++ b/tests/HelpTest.cpp @@ -270,6 +270,54 @@ TEST(THelp, SetLower) { EXPECT_THAT(help, HasSubstr("THREE")); } +TEST(THelp, OnlyOneHelp) { + CLI::App app{"My prog"}; + + /* It is not supported to add more than one help flag. */ + EXPECT_THROW(app.add_help_flag("--yelp", "Alias for help"), CLI::IncorrectConstruction); +} + +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 input{"--help"}; + try { + app.parse(input); + } catch(const CLI::ParseError &e) { + EXPECT_EQ(static_cast(CLI::ExitCodes::Extras), e.get_exit_code()); + } +} + +TEST(THelp, CustomHelp) { + CLI::App app{"My prog", false}; + + CLI::Option *help_option = app.add_help_flag("--yelp", "display help and exit"); + EXPECT_EQ(app.get_help_ptr(), help_option); + EXPECT_THROW(app.add_help_flag("--help", "Alias for yelp"), CLI::IncorrectConstruction); + + 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 input{"--yelp"}; + try { + app.parse(input); + } catch(const CLI::CallForHelp &e) { + EXPECT_EQ(static_cast(CLI::ExitCodes::Success), e.get_exit_code()); + } +} + TEST(Exit, ErrorWithHelp) { CLI::App app{"My prog"}; From 17eeca0f59bdfa6b932667e44943b0c1942692ef Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Mon, 20 Nov 2017 09:21:35 -0500 Subject: [PATCH 2/5] Adding a little info to the docs --- README.md | 2 ++ include/CLI/App.hpp | 1 + 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index c230593cc..c780963ce 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,8 @@ app.add_set(option_name, app.add_set_ignore_case(... // String only +app.add_help_flag(name, optional_discription); + App* subcom = app.add_subcommand(name, discription); ``` diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index bb6e39982..d2deee858 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -322,6 +322,7 @@ class App { return opt; } + /// Add a help flag, currently throws an error if already set Option *add_help_flag(std::string name, std::string description = "") { if(help_ptr_) throw IncorrectConstruction("Help flag already initialized"); From 264c4e5b4602701b0c09f49564e8e1aa8169f6e8 Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Mon, 20 Nov 2017 09:29:49 -0500 Subject: [PATCH 3/5] Using add_help_flag internally --- include/CLI/App.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index d2deee858..81a73ad9b 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -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"); + add_help_flag("-h,--help", "Print this help message and exit"); } public: From 9d51c0e284cc0f34178675e7af0ec9ae39cc55c9 Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Mon, 20 Nov 2017 10:43:53 -0500 Subject: [PATCH 4/5] Deregister help if added previously --- include/CLI/App.hpp | 6 +++--- tests/HelpTest.cpp | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 81a73ad9b..2452c201c 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -322,10 +322,10 @@ class App { return opt; } - /// Add a help flag, currently throws an error if already set + /// Add a help flag, replaced the existing one if present Option *add_help_flag(std::string name, std::string description = "") { - if(help_ptr_) - throw IncorrectConstruction("Help flag already initialized"); + if(help_ptr_ != nullptr) + remove_option(help_ptr_); help_ptr_ = add_flag(name, description); return help_ptr_; } diff --git a/tests/HelpTest.cpp b/tests/HelpTest.cpp index d999191f5..f58f9c529 100644 --- a/tests/HelpTest.cpp +++ b/tests/HelpTest.cpp @@ -273,8 +273,13 @@ TEST(THelp, SetLower) { TEST(THelp, OnlyOneHelp) { CLI::App app{"My prog"}; - /* It is not supported to add more than one help flag. */ - EXPECT_THROW(app.add_help_flag("--yelp", "Alias for help"), CLI::IncorrectConstruction); + // It is not supported to have more than one help flag, last one wins + app.add_help_flag("--help", "No short name allowed"); + app.add_help_flag("--yelp", "Alias for help"); + + std::vector input{"--help"}; + EXPECT_THROW(app.parse(input), CLI::ExtrasError); + } TEST(THelp, NoHelp) { @@ -300,7 +305,6 @@ TEST(THelp, CustomHelp) { CLI::Option *help_option = app.add_help_flag("--yelp", "display help and exit"); EXPECT_EQ(app.get_help_ptr(), help_option); - EXPECT_THROW(app.add_help_flag("--help", "Alias for yelp"), CLI::IncorrectConstruction); std::string help = app.help(); From aba829b1ea1b108d4e58d7569d8a7403fb830a3e Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Mon, 20 Nov 2017 11:07:06 -0500 Subject: [PATCH 5/5] set_help_flag now can replace a flag if needed, or remove it --- README.md | 6 ++---- include/CLI/App.hpp | 16 +++++++++++----- tests/HelpTest.cpp | 26 ++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c780963ce..5c241e742 100644 --- a/README.md +++ b/README.md @@ -132,8 +132,6 @@ app.add_set(option_name, app.add_set_ignore_case(... // String only -app.add_help_flag(name, optional_discription); - App* subcom = app.add_subcommand(name, discription); ``` @@ -163,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` 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` of results. On the command line, options can be given as: @@ -246,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. diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 2452c201c..a243e4f5f 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -137,7 +137,7 @@ class App { App(std::string description_, bool help, detail::enabler) : description_(std::move(description_)) { if(help) - add_help_flag("-h,--help", "Print this help message and exit"); + set_help_flag("-h,--help", "Print this help message and exit"); } public: @@ -322,11 +322,17 @@ class App { return opt; } - /// Add a help flag, replaced the existing one if present - Option *add_help_flag(std::string name, std::string description = "") { - if(help_ptr_ != nullptr) + /// 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_ = add_flag(name, description); + help_ptr_ = nullptr; + } + + // Empty name will simply remove the help flag + if(!name.empty()) + help_ptr_ = add_flag(name, description); + return help_ptr_; } diff --git a/tests/HelpTest.cpp b/tests/HelpTest.cpp index f58f9c529..c0b29efbd 100644 --- a/tests/HelpTest.cpp +++ b/tests/HelpTest.cpp @@ -274,12 +274,30 @@ TEST(THelp, OnlyOneHelp) { CLI::App app{"My prog"}; // It is not supported to have more than one help flag, last one wins - app.add_help_flag("--help", "No short name allowed"); - app.add_help_flag("--yelp", "Alias for help"); + app.set_help_flag("--help", "No short name allowed"); + app.set_help_flag("--yelp", "Alias for help"); std::vector 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 input{"--help"}; + try { + app.parse(input); + } catch(const CLI::ParseError &e) { + EXPECT_EQ(static_cast(CLI::ExitCodes::Extras), e.get_exit_code()); + } } TEST(THelp, NoHelp) { @@ -303,7 +321,7 @@ TEST(THelp, NoHelp) { TEST(THelp, CustomHelp) { CLI::App app{"My prog", false}; - CLI::Option *help_option = app.add_help_flag("--yelp", "display help and exit"); + 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();