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 | 2 | |
| 3 | 3 | class MyFormatter : public CLI::Formatter { |
| 4 | 4 | public: |
| 5 | + using Formatter::Formatter; | |
| 5 | 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 | 10 | CLI::App app; |
| 10 | 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 | 15 | app.formatter(fmt); |
| 15 | 16 | |
| 16 | 17 | app.add_flag("--flag", "This is a flag"); | ... | ... |
include/CLI/App.hpp
| ... | ... | @@ -106,8 +106,8 @@ class App { |
| 106 | 106 | /// A pointer to the help all flag if there is one INHERITABLE |
| 107 | 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 | 112 | /// The error message printing function INHERITABLE |
| 113 | 113 | std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple; |
| ... | ... | @@ -262,11 +262,17 @@ class App { |
| 262 | 262 | } |
| 263 | 263 | |
| 264 | 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 | 266 | formatter_ = fmt; |
| 267 | 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 | 276 | /// Check to see if this subcommand was parsed, true only if received on command line. |
| 271 | 277 | bool parsed() const { return parsed_; } |
| 272 | 278 | |
| ... | ... | @@ -1071,13 +1077,16 @@ class App { |
| 1071 | 1077 | if(!selected_subcommands.empty()) |
| 1072 | 1078 | return selected_subcommands.at(0)->help(prev); |
| 1073 | 1079 | else |
| 1074 | - return formatter_(this, prev, mode); | |
| 1080 | + return formatter_->make_help(this, prev, mode); | |
| 1075 | 1081 | } |
| 1076 | 1082 | |
| 1077 | 1083 | ///@} |
| 1078 | 1084 | /// @name Getters |
| 1079 | 1085 | ///@{ |
| 1080 | 1086 | |
| 1087 | + /// Access the formatter | |
| 1088 | + std::shared_ptr<FormatterBase> get_formatter() const { return formatter_; } | |
| 1089 | + | |
| 1081 | 1090 | /// Get the app or subcommand description |
| 1082 | 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 | 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 | 118 | // This immediatly forwards to the make_expanded method. This is done this way so that subcommands can |
| 119 | 119 | // have overridden formatters | ... | ... |
include/CLI/FormatterFwd.hpp
| ... | ... | @@ -21,10 +21,15 @@ class App; |
| 21 | 21 | enum class AppFormatMode { |
| 22 | 22 | Normal, //< The normal, detailed help |
| 23 | 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 | 33 | /// @name Options |
| 29 | 34 | ///@{ |
| 30 | 35 | |
| ... | ... | @@ -40,9 +45,14 @@ class Formatter { |
| 40 | 45 | ///@{ |
| 41 | 46 | |
| 42 | 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 | 57 | /// @name Setters |
| 48 | 58 | ///@{ |
| ... | ... | @@ -68,6 +78,30 @@ class Formatter { |
| 68 | 78 | /// Get the current column width |
| 69 | 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 | 105 | /// @name Overridables |
| 72 | 106 | ///@{ |
| 73 | 107 | |
| ... | ... | @@ -102,7 +136,7 @@ class Formatter { |
| 102 | 136 | virtual std::string make_usage(const App *app, std::string name) const; |
| 103 | 137 | |
| 104 | 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 | 142 | /// @name Options | ... | ... |
tests/FormatterTest.cpp
| ... | ... | @@ -11,9 +11,28 @@ |
| 11 | 11 | using ::testing::HasSubstr; |
| 12 | 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 | 23 | TEST(Formatter, Nothing) { |
| 15 | 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 | 36 | app.formatter( |
| 18 | 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 | 44 | TEST(Formatter, OptCustomize) { |
| 26 | 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 | 50 | app.formatter(optfmt); |
| 32 | 51 | |
| 33 | 52 | int v; |
| ... | ... | @@ -44,13 +63,33 @@ TEST(Formatter, OptCustomize) { |
| 44 | 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 | 87 | CLI::App app{"My prog"}; |
| 49 | 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 | 93 | app.formatter(appfmt); |
| 55 | 94 | |
| 56 | 95 | app.add_subcommand("subcom2", "This"); |
| ... | ... | @@ -66,6 +105,26 @@ TEST(Formatter, AptCustomize) { |
| 66 | 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 | 128 | TEST(Formatter, AllSub) { |
| 70 | 129 | CLI::App app{"My prog"}; |
| 71 | 130 | CLI::App *sub = app.add_subcommand("subcom", "This"); | ... | ... |