Skip to content

Commit

Permalink
Adding access to current formatter, using shared pointer to link all …
Browse files Browse the repository at this point in the history
…formatters
  • Loading branch information
henryiii committed May 7, 2018
1 parent fe04a7c commit 33fcce3
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 20 deletions.
5 changes: 3 additions & 2 deletions examples/formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

class MyFormatter : public CLI::Formatter {
public:
using Formatter::Formatter;
std::string make_option_opts(const CLI::Option *) const override { return " OPTION"; }
};

int main(int argc, char **argv) {
CLI::App app;
app.set_help_all_flag("--help-all", "Show all help");

MyFormatter fmt;
fmt.column_width(15);
auto fmt = std::make_shared<MyFormatter>();
fmt->column_width(15);
app.formatter(fmt);

app.add_flag("--flag", "This is a flag");
Expand Down
17 changes: 13 additions & 4 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ class App {
/// A pointer to the help all flag if there is one INHERITABLE
Option *help_all_ptr_{nullptr};

/// This is the formatter for help printing. Default provided. INHERITABLE
std::function<std::string(const App *, std::string, AppFormatMode)> formatter_{Formatter()};
/// This is the formatter for help printing. Default provided. INHERITABLE (same pointer)
std::shared_ptr<FormatterBase> formatter_{new Formatter()};

/// The error message printing function INHERITABLE
std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple;
Expand Down Expand Up @@ -262,11 +262,17 @@ class App {
}

/// Set the help formatter
App *formatter(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) {
App *formatter(std::shared_ptr<FormatterBase> fmt) {
formatter_ = fmt;
return this;
}

/// Set the help formatter
App *formatter(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) {
formatter_ = std::make_shared<FormatterLambda>(fmt);
return this;
}

/// Check to see if this subcommand was parsed, true only if received on command line.
bool parsed() const { return parsed_; }

Expand Down Expand Up @@ -1071,13 +1077,16 @@ class App {
if(!selected_subcommands.empty())
return selected_subcommands.at(0)->help(prev);
else
return formatter_(this, prev, mode);
return formatter_->make_help(this, prev, mode);
}

///@}
/// @name Getters
///@{

/// Access the formatter
std::shared_ptr<FormatterBase> get_formatter() const { return formatter_; }

/// Get the app or subcommand description
std::string get_description() const { return description_; }

Expand Down
2 changes: 1 addition & 1 deletion include/CLI/Formatter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ inline std::string Formatter::make_footer(const App *app) const {
return "";
}

inline std::string Formatter::operator()(const App *app, std::string name, AppFormatMode mode) const {
inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {

// This immediatly forwards to the make_expanded method. This is done this way so that subcommands can
// have overridden formatters
Expand Down
46 changes: 40 additions & 6 deletions include/CLI/FormatterFwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ class App;
enum class AppFormatMode {
Normal, //< The normal, detailed help
All, //< A fully expanded help
Sub, //< Used when printed as part of expanded subcommand
Sub, //< Used when printed as part of expanded subcommand
};

class Formatter {
/// This is the minimum requirements to run a formatter.
///
/// A user can subclass this is if they do not care at all
/// about the structure in CLI::Formatter.
class FormatterBase {
protected:
/// @name Options
///@{

Expand All @@ -40,9 +45,14 @@ class Formatter {
///@{

public:
Formatter() = default;
Formatter(const Formatter &) = default;
Formatter(Formatter &&) = default;
FormatterBase() = default;
FormatterBase(const FormatterBase &) = default;
FormatterBase(FormatterBase &&) = default;
virtual ~FormatterBase() = default;

/// This is the key method that puts together help
virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0;

///@}
/// @name Setters
///@{
Expand All @@ -68,6 +78,30 @@ class Formatter {
/// Get the current column width
size_t get_column_width() const { return column_width_; }

///@}
};

/// This is a specialty override for lambda functions
class FormatterLambda final : public FormatterBase {
using funct_t = std::function<std::string(const App *, std::string, AppFormatMode)>;

funct_t lambda_;

public:
explicit FormatterLambda(funct_t funct) : lambda_(funct) {}

/// This will simply call the lambda function
std::string make_help(const App *app, std::string name, AppFormatMode mode) const override {
return lambda_(app, name, mode);
}
};

class Formatter : public FormatterBase {
public:
Formatter() = default;
Formatter(const Formatter &) = default;
Formatter(Formatter &&) = default;

/// @name Overridables
///@{

Expand Down Expand Up @@ -102,7 +136,7 @@ class Formatter {
virtual std::string make_usage(const App *app, std::string name) const;

/// This puts everything together
virtual std::string operator()(const App *, std::string, AppFormatMode) const;
std::string make_help(const App *, std::string, AppFormatMode) const override;

///@}
/// @name Options
Expand Down
73 changes: 66 additions & 7 deletions tests/FormatterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,28 @@
using ::testing::HasSubstr;
using ::testing::Not;

class SimpleFormatter : public CLI::FormatterBase {
public:
SimpleFormatter() : FormatterBase() {}

std::string make_help(const CLI::App *, std::string, CLI::AppFormatMode) const override {
return "This is really simple";
}
};

TEST(Formatter, Nothing) {
CLI::App app{"My prog"};

app.formatter(std::make_shared<SimpleFormatter>());

std::string help = app.help();

EXPECT_EQ(help, "This is really simple");
}

TEST(Formatter, NothingLambda) {
CLI::App app{"My prog"};

app.formatter(
[](const CLI::App *, std::string, CLI::AppFormatMode) { return std::string("This is really simple"); });

Expand All @@ -25,9 +44,9 @@ TEST(Formatter, Nothing) {
TEST(Formatter, OptCustomize) {
CLI::App app{"My prog"};

CLI::Formatter optfmt;
optfmt.column_width(25);
optfmt.label("REQUIRED", "(MUST HAVE)");
auto optfmt = std::make_shared<CLI::Formatter>();
optfmt->column_width(25);
optfmt->label("REQUIRED", "(MUST HAVE)");
app.formatter(optfmt);

int v;
Expand All @@ -44,13 +63,33 @@ TEST(Formatter, OptCustomize) {
" --opt INT (MUST HAVE) Something\n");
}

TEST(Formatter, AptCustomize) {
TEST(Formatter, OptCustomizeSimple) {
CLI::App app{"My prog"};

app.get_formatter()->column_width(25);
app.get_formatter()->label("REQUIRED", "(MUST HAVE)");

int v;
app.add_option("--opt", v, "Something")->required();

std::string help = app.help();

EXPECT_THAT(help, HasSubstr("(MUST HAVE)"));
EXPECT_EQ(help,
"My prog\n"
"Usage: [OPTIONS]\n\n"
"Options:\n"
" -h,--help Print this help message and exit\n"
" --opt INT (MUST HAVE) Something\n");
}

TEST(Formatter, AppCustomize) {
CLI::App app{"My prog"};
app.add_subcommand("subcom1", "This");

CLI::Formatter appfmt;
appfmt.column_width(20);
appfmt.label("Usage", "Run");
auto appfmt = std::make_shared<CLI::Formatter>();
appfmt->column_width(20);
appfmt->label("Usage", "Run");
app.formatter(appfmt);

app.add_subcommand("subcom2", "This");
Expand All @@ -66,6 +105,26 @@ TEST(Formatter, AptCustomize) {
" subcom2 This\n");
}

TEST(Formatter, AppCustomizeSimple) {
CLI::App app{"My prog"};
app.add_subcommand("subcom1", "This");

app.get_formatter()->column_width(20);
app.get_formatter()->label("Usage", "Run");

app.add_subcommand("subcom2", "This");

std::string help = app.help();
EXPECT_EQ(help,
"My prog\n"
"Run: [OPTIONS] [SUBCOMMAND]\n\n"
"Options:\n"
" -h,--help Print this help message and exit\n\n"
"Subcommands:\n"
" subcom1 This\n"
" subcom2 This\n");
}

TEST(Formatter, AllSub) {
CLI::App app{"My prog"};
CLI::App *sub = app.add_subcommand("subcom", "This");
Expand Down

0 comments on commit 33fcce3

Please sign in to comment.