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