Commit 0d9a33d4cae1f762c73eae750db141486b90354b
Committed by
Henry Schreiner
1 parent
aac95750
Adding access to current formatter, using shared pointer to link all formatters
Showing
5 changed files
with
123 additions
and
20 deletions
examples/formatter.cpp
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | 2 | ||
| 3 | class MyFormatter : public CLI::Formatter { | 3 | class MyFormatter : public CLI::Formatter { |
| 4 | public: | 4 | public: |
| 5 | + using Formatter::Formatter; | ||
| 5 | std::string make_option_opts(const CLI::Option *) const override { return " OPTION"; } | 6 | std::string make_option_opts(const CLI::Option *) const override { return " OPTION"; } |
| 6 | }; | 7 | }; |
| 7 | 8 | ||
| @@ -9,8 +10,8 @@ int main(int argc, char **argv) { | @@ -9,8 +10,8 @@ int main(int argc, char **argv) { | ||
| 9 | CLI::App app; | 10 | CLI::App app; |
| 10 | app.set_help_all_flag("--help-all", "Show all help"); | 11 | app.set_help_all_flag("--help-all", "Show all help"); |
| 11 | 12 | ||
| 12 | - MyFormatter fmt; | ||
| 13 | - fmt.column_width(15); | 13 | + auto fmt = std::make_shared<MyFormatter>(); |
| 14 | + fmt->column_width(15); | ||
| 14 | app.formatter(fmt); | 15 | app.formatter(fmt); |
| 15 | 16 | ||
| 16 | app.add_flag("--flag", "This is a flag"); | 17 | app.add_flag("--flag", "This is a flag"); |
include/CLI/App.hpp
| @@ -106,8 +106,8 @@ class App { | @@ -106,8 +106,8 @@ class App { | ||
| 106 | /// A pointer to the help all flag if there is one INHERITABLE | 106 | /// A pointer to the help all flag if there is one INHERITABLE |
| 107 | Option *help_all_ptr_{nullptr}; | 107 | Option *help_all_ptr_{nullptr}; |
| 108 | 108 | ||
| 109 | - /// This is the formatter for help printing. Default provided. INHERITABLE | ||
| 110 | - std::function<std::string(const App *, std::string, AppFormatMode)> formatter_{Formatter()}; | 109 | + /// This is the formatter for help printing. Default provided. INHERITABLE (same pointer) |
| 110 | + std::shared_ptr<FormatterBase> formatter_{new Formatter()}; | ||
| 111 | 111 | ||
| 112 | /// The error message printing function INHERITABLE | 112 | /// The error message printing function INHERITABLE |
| 113 | std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple; | 113 | std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple; |
| @@ -262,11 +262,17 @@ class App { | @@ -262,11 +262,17 @@ class App { | ||
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | /// Set the help formatter | 264 | /// Set the help formatter |
| 265 | - App *formatter(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) { | 265 | + App *formatter(std::shared_ptr<FormatterBase> fmt) { |
| 266 | formatter_ = fmt; | 266 | formatter_ = fmt; |
| 267 | return this; | 267 | return this; |
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | + /// Set the help formatter | ||
| 271 | + App *formatter(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) { | ||
| 272 | + formatter_ = std::make_shared<FormatterLambda>(fmt); | ||
| 273 | + return this; | ||
| 274 | + } | ||
| 275 | + | ||
| 270 | /// Check to see if this subcommand was parsed, true only if received on command line. | 276 | /// Check to see if this subcommand was parsed, true only if received on command line. |
| 271 | bool parsed() const { return parsed_; } | 277 | bool parsed() const { return parsed_; } |
| 272 | 278 | ||
| @@ -1071,13 +1077,16 @@ class App { | @@ -1071,13 +1077,16 @@ class App { | ||
| 1071 | if(!selected_subcommands.empty()) | 1077 | if(!selected_subcommands.empty()) |
| 1072 | return selected_subcommands.at(0)->help(prev); | 1078 | return selected_subcommands.at(0)->help(prev); |
| 1073 | else | 1079 | else |
| 1074 | - return formatter_(this, prev, mode); | 1080 | + return formatter_->make_help(this, prev, mode); |
| 1075 | } | 1081 | } |
| 1076 | 1082 | ||
| 1077 | ///@} | 1083 | ///@} |
| 1078 | /// @name Getters | 1084 | /// @name Getters |
| 1079 | ///@{ | 1085 | ///@{ |
| 1080 | 1086 | ||
| 1087 | + /// Access the formatter | ||
| 1088 | + std::shared_ptr<FormatterBase> get_formatter() const { return formatter_; } | ||
| 1089 | + | ||
| 1081 | /// Get the app or subcommand description | 1090 | /// Get the app or subcommand description |
| 1082 | std::string get_description() const { return description_; } | 1091 | std::string get_description() const { return description_; } |
| 1083 | 1092 |
include/CLI/Formatter.hpp
| @@ -113,7 +113,7 @@ inline std::string Formatter::make_footer(const App *app) const { | @@ -113,7 +113,7 @@ inline std::string Formatter::make_footer(const App *app) const { | ||
| 113 | return ""; | 113 | return ""; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | -inline std::string Formatter::operator()(const App *app, std::string name, AppFormatMode mode) const { | 116 | +inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const { |
| 117 | 117 | ||
| 118 | // This immediatly forwards to the make_expanded method. This is done this way so that subcommands can | 118 | // This immediatly forwards to the make_expanded method. This is done this way so that subcommands can |
| 119 | // have overridden formatters | 119 | // have overridden formatters |
include/CLI/FormatterFwd.hpp
| @@ -21,10 +21,15 @@ class App; | @@ -21,10 +21,15 @@ class App; | ||
| 21 | enum class AppFormatMode { | 21 | enum class AppFormatMode { |
| 22 | Normal, //< The normal, detailed help | 22 | Normal, //< The normal, detailed help |
| 23 | All, //< A fully expanded help | 23 | All, //< A fully expanded help |
| 24 | - Sub, //< Used when printed as part of expanded subcommand | 24 | + Sub, //< Used when printed as part of expanded subcommand |
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | -class Formatter { | 27 | +/// This is the minimum requirements to run a formatter. |
| 28 | +/// | ||
| 29 | +/// A user can subclass this is if they do not care at all | ||
| 30 | +/// about the structure in CLI::Formatter. | ||
| 31 | +class FormatterBase { | ||
| 32 | + protected: | ||
| 28 | /// @name Options | 33 | /// @name Options |
| 29 | ///@{ | 34 | ///@{ |
| 30 | 35 | ||
| @@ -40,9 +45,14 @@ class Formatter { | @@ -40,9 +45,14 @@ class Formatter { | ||
| 40 | ///@{ | 45 | ///@{ |
| 41 | 46 | ||
| 42 | public: | 47 | public: |
| 43 | - Formatter() = default; | ||
| 44 | - Formatter(const Formatter &) = default; | ||
| 45 | - Formatter(Formatter &&) = default; | 48 | + FormatterBase() = default; |
| 49 | + FormatterBase(const FormatterBase &) = default; | ||
| 50 | + FormatterBase(FormatterBase &&) = default; | ||
| 51 | + virtual ~FormatterBase() = default; | ||
| 52 | + | ||
| 53 | + /// This is the key method that puts together help | ||
| 54 | + virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0; | ||
| 55 | + | ||
| 46 | ///@} | 56 | ///@} |
| 47 | /// @name Setters | 57 | /// @name Setters |
| 48 | ///@{ | 58 | ///@{ |
| @@ -68,6 +78,30 @@ class Formatter { | @@ -68,6 +78,30 @@ class Formatter { | ||
| 68 | /// Get the current column width | 78 | /// Get the current column width |
| 69 | size_t get_column_width() const { return column_width_; } | 79 | size_t get_column_width() const { return column_width_; } |
| 70 | 80 | ||
| 81 | + ///@} | ||
| 82 | +}; | ||
| 83 | + | ||
| 84 | +/// This is a specialty override for lambda functions | ||
| 85 | +class FormatterLambda final : public FormatterBase { | ||
| 86 | + using funct_t = std::function<std::string(const App *, std::string, AppFormatMode)>; | ||
| 87 | + | ||
| 88 | + funct_t lambda_; | ||
| 89 | + | ||
| 90 | + public: | ||
| 91 | + explicit FormatterLambda(funct_t funct) : lambda_(funct) {} | ||
| 92 | + | ||
| 93 | + /// This will simply call the lambda function | ||
| 94 | + std::string make_help(const App *app, std::string name, AppFormatMode mode) const override { | ||
| 95 | + return lambda_(app, name, mode); | ||
| 96 | + } | ||
| 97 | +}; | ||
| 98 | + | ||
| 99 | +class Formatter : public FormatterBase { | ||
| 100 | + public: | ||
| 101 | + Formatter() = default; | ||
| 102 | + Formatter(const Formatter &) = default; | ||
| 103 | + Formatter(Formatter &&) = default; | ||
| 104 | + | ||
| 71 | /// @name Overridables | 105 | /// @name Overridables |
| 72 | ///@{ | 106 | ///@{ |
| 73 | 107 | ||
| @@ -102,7 +136,7 @@ class Formatter { | @@ -102,7 +136,7 @@ class Formatter { | ||
| 102 | virtual std::string make_usage(const App *app, std::string name) const; | 136 | virtual std::string make_usage(const App *app, std::string name) const; |
| 103 | 137 | ||
| 104 | /// This puts everything together | 138 | /// This puts everything together |
| 105 | - virtual std::string operator()(const App *, std::string, AppFormatMode) const; | 139 | + std::string make_help(const App *, std::string, AppFormatMode) const override; |
| 106 | 140 | ||
| 107 | ///@} | 141 | ///@} |
| 108 | /// @name Options | 142 | /// @name Options |
tests/FormatterTest.cpp
| @@ -11,9 +11,28 @@ | @@ -11,9 +11,28 @@ | ||
| 11 | using ::testing::HasSubstr; | 11 | using ::testing::HasSubstr; |
| 12 | using ::testing::Not; | 12 | using ::testing::Not; |
| 13 | 13 | ||
| 14 | +class SimpleFormatter : public CLI::FormatterBase { | ||
| 15 | + public: | ||
| 16 | + SimpleFormatter() : FormatterBase() {} | ||
| 17 | + | ||
| 18 | + std::string make_help(const CLI::App *, std::string, CLI::AppFormatMode) const override { | ||
| 19 | + return "This is really simple"; | ||
| 20 | + } | ||
| 21 | +}; | ||
| 22 | + | ||
| 14 | TEST(Formatter, Nothing) { | 23 | TEST(Formatter, Nothing) { |
| 15 | CLI::App app{"My prog"}; | 24 | CLI::App app{"My prog"}; |
| 16 | 25 | ||
| 26 | + app.formatter(std::make_shared<SimpleFormatter>()); | ||
| 27 | + | ||
| 28 | + std::string help = app.help(); | ||
| 29 | + | ||
| 30 | + EXPECT_EQ(help, "This is really simple"); | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +TEST(Formatter, NothingLambda) { | ||
| 34 | + CLI::App app{"My prog"}; | ||
| 35 | + | ||
| 17 | app.formatter( | 36 | app.formatter( |
| 18 | [](const CLI::App *, std::string, CLI::AppFormatMode) { return std::string("This is really simple"); }); | 37 | [](const CLI::App *, std::string, CLI::AppFormatMode) { return std::string("This is really simple"); }); |
| 19 | 38 | ||
| @@ -25,9 +44,9 @@ TEST(Formatter, Nothing) { | @@ -25,9 +44,9 @@ TEST(Formatter, Nothing) { | ||
| 25 | TEST(Formatter, OptCustomize) { | 44 | TEST(Formatter, OptCustomize) { |
| 26 | CLI::App app{"My prog"}; | 45 | CLI::App app{"My prog"}; |
| 27 | 46 | ||
| 28 | - CLI::Formatter optfmt; | ||
| 29 | - optfmt.column_width(25); | ||
| 30 | - optfmt.label("REQUIRED", "(MUST HAVE)"); | 47 | + auto optfmt = std::make_shared<CLI::Formatter>(); |
| 48 | + optfmt->column_width(25); | ||
| 49 | + optfmt->label("REQUIRED", "(MUST HAVE)"); | ||
| 31 | app.formatter(optfmt); | 50 | app.formatter(optfmt); |
| 32 | 51 | ||
| 33 | int v; | 52 | int v; |
| @@ -44,13 +63,33 @@ TEST(Formatter, OptCustomize) { | @@ -44,13 +63,33 @@ TEST(Formatter, OptCustomize) { | ||
| 44 | " --opt INT (MUST HAVE) Something\n"); | 63 | " --opt INT (MUST HAVE) Something\n"); |
| 45 | } | 64 | } |
| 46 | 65 | ||
| 47 | -TEST(Formatter, AptCustomize) { | 66 | +TEST(Formatter, OptCustomizeSimple) { |
| 67 | + CLI::App app{"My prog"}; | ||
| 68 | + | ||
| 69 | + app.get_formatter()->column_width(25); | ||
| 70 | + app.get_formatter()->label("REQUIRED", "(MUST HAVE)"); | ||
| 71 | + | ||
| 72 | + int v; | ||
| 73 | + app.add_option("--opt", v, "Something")->required(); | ||
| 74 | + | ||
| 75 | + std::string help = app.help(); | ||
| 76 | + | ||
| 77 | + EXPECT_THAT(help, HasSubstr("(MUST HAVE)")); | ||
| 78 | + EXPECT_EQ(help, | ||
| 79 | + "My prog\n" | ||
| 80 | + "Usage: [OPTIONS]\n\n" | ||
| 81 | + "Options:\n" | ||
| 82 | + " -h,--help Print this help message and exit\n" | ||
| 83 | + " --opt INT (MUST HAVE) Something\n"); | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +TEST(Formatter, AppCustomize) { | ||
| 48 | CLI::App app{"My prog"}; | 87 | CLI::App app{"My prog"}; |
| 49 | app.add_subcommand("subcom1", "This"); | 88 | app.add_subcommand("subcom1", "This"); |
| 50 | 89 | ||
| 51 | - CLI::Formatter appfmt; | ||
| 52 | - appfmt.column_width(20); | ||
| 53 | - appfmt.label("Usage", "Run"); | 90 | + auto appfmt = std::make_shared<CLI::Formatter>(); |
| 91 | + appfmt->column_width(20); | ||
| 92 | + appfmt->label("Usage", "Run"); | ||
| 54 | app.formatter(appfmt); | 93 | app.formatter(appfmt); |
| 55 | 94 | ||
| 56 | app.add_subcommand("subcom2", "This"); | 95 | app.add_subcommand("subcom2", "This"); |
| @@ -66,6 +105,26 @@ TEST(Formatter, AptCustomize) { | @@ -66,6 +105,26 @@ TEST(Formatter, AptCustomize) { | ||
| 66 | " subcom2 This\n"); | 105 | " subcom2 This\n"); |
| 67 | } | 106 | } |
| 68 | 107 | ||
| 108 | +TEST(Formatter, AppCustomizeSimple) { | ||
| 109 | + CLI::App app{"My prog"}; | ||
| 110 | + app.add_subcommand("subcom1", "This"); | ||
| 111 | + | ||
| 112 | + app.get_formatter()->column_width(20); | ||
| 113 | + app.get_formatter()->label("Usage", "Run"); | ||
| 114 | + | ||
| 115 | + app.add_subcommand("subcom2", "This"); | ||
| 116 | + | ||
| 117 | + std::string help = app.help(); | ||
| 118 | + EXPECT_EQ(help, | ||
| 119 | + "My prog\n" | ||
| 120 | + "Run: [OPTIONS] [SUBCOMMAND]\n\n" | ||
| 121 | + "Options:\n" | ||
| 122 | + " -h,--help Print this help message and exit\n\n" | ||
| 123 | + "Subcommands:\n" | ||
| 124 | + " subcom1 This\n" | ||
| 125 | + " subcom2 This\n"); | ||
| 126 | +} | ||
| 127 | + | ||
| 69 | TEST(Formatter, AllSub) { | 128 | TEST(Formatter, AllSub) { |
| 70 | CLI::App app{"My prog"}; | 129 | CLI::App app{"My prog"}; |
| 71 | CLI::App *sub = app.add_subcommand("subcom", "This"); | 130 | CLI::App *sub = app.add_subcommand("subcom", "This"); |