Commit 0d9a33d4cae1f762c73eae750db141486b90354b

Authored by Henry Fredrick Schreiner
Committed by Henry Schreiner
1 parent aac95750

Adding access to current formatter, using shared pointer to link all formatters

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");