1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 12:13:52 +00:00

Just use Formatter

Removing OptionFormatter

Rename files

Rename to just Formatter

Remove OptionFormatMode (just needs a bool)

Renaming option functions on formatter
This commit is contained in:
Henry Fredrick Schreiner 2018-04-30 00:33:42 -04:00 committed by Henry Schreiner
parent 89975e51e7
commit af2ed66d6e
12 changed files with 341 additions and 447 deletions

View File

@ -1,8 +1,8 @@
### Version 1.6: Formatters
### Version 1.6: Formatting
Added a new formatting system. You can now set the formatter on Apps and Options.
Added a new formatting system. You can now set the formatter on Apps.
* Added `AppFormatter` and `OptionFormatter`, and `formatter` slot
* Added `CLI::Formatter` and `formatter` slot for apps, inherited.
* Added `help_all` support (not added by default)
* Added filter argument to `get_subcommands`, `get_options`; use empty filter `{}` to avoid filtering
* Added `get_groups()` to get groups

View File

@ -193,7 +193,6 @@ The add commands return a pointer to an internally stored `Option`. If you set t
* `->check(CLI::Range(min,max))`: Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.
* `->transform(std::string(std::string))`: Converts the input string into the output string, in-place in the parsed options.
* `->configurable(false)`: Disable this option from being in an ini configuration file.
* `->formatter(fmt)`: Set the formatter, with signature `std::string(const Option*, OptionFormatMode)`. See Formatting for more details.
These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results.
@ -306,15 +305,13 @@ The default settings for options are inherited to subcommands, as well.
## Formatting
The job of formatting help printouts is delegated to a formatter callable object on Apps and Options. You are free to replace either formatter by calling `formatter(fmt)` on an `App` or `Option`.
CLI11 comes with a default App formatter functional, `AppFormatter`, and a default `OptionFormatter` as well. They are both customizable; you can set `label(key, value)` to replace the default labels like `REQUIRED`, and `column_width(n)` to set the width of the columns before you add the functional to the app or option. You can also override almost any stage of the formatting process in a subclass of either formatter. If you want to make a new formatter from scratch, you can do
that too; you just need to implement the correct signature. The first argument is a const pointer to the App or Option in question. The App formatter will get a `std::string` usage name as the second option, and both end with with a mode. Both formatters return a `std::string`.
The job of formatting help printouts is delegated to a formatter callable object on Apps and Options. You are free to replace either formatter by calling `formatter(fmt)` on an `App`, where fmt is any copyable callable with the correct signature.
CLI11 comes with a default App formatter functional, `Formatter`. It is customizable; you can set `label(key, value)` to replace the default labels like `REQUIRED`, and `column_width(n)` to set the width of the columns before you add the functional to the app or option. You can also override almost any stage of the formatting process in a subclass of either formatter. If you want to make a new formatter from scratch, you can do
that too; you just need to implement the correct signature. The first argument is a const pointer to the in question. The formatter will get a `std::string` usage name as the second option, and a `AppFormatMode` mode for the final option. It should return a `std::string`.
The `AppFormatMode` can be `Normal`, `All`, or `Sub`, and it indicates the situation the help was called in. `Sub` is optional, but the default formatter uses it to make sure expanded subcommands are called with
their own formatter since you can't access anything but the call operator once a formatter has been set.
The `OptionFormatMode` also has three values, depending on the calling situation: `Positional`, `Optional`, and `Usage`. The default formatter uses these to print out the option in each of these situations.
## Subclassing
The App class was designed allow toolkits to subclass it, to provide preset default options (see above) and setup/teardown code. Subcommands remain an unsubclassed `App`, since those are not expected to need setup and teardown. The default `App` only adds a help flag, `-h,--help`, than can removed/replaced using `.set_help_flag(name, help_string)`. You can also set a help-all flag with `.set_help_all_flag(name, help_string)`; this will expand the subcommands (one level only). You can remove options if you have pointers to them using `.remove_option(opt)`. You can add a `pre_callback` override to customize the after parse

View File

@ -1,17 +1,15 @@
#include <CLI/CLI.hpp>
class MyFormatter : public CLI::OptionFormatter {
class MyFormatter : public CLI::Formatter {
public:
std::string make_opts(const CLI::Option *) const override { return " OPTION"; }
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");
app.option_defaults()->formatter(MyFormatter());
CLI::AppFormatter fmt;
MyFormatter fmt;
fmt.column_width(15);
app.formatter(fmt);

View File

@ -24,7 +24,7 @@
#include "CLI/Split.hpp"
#include "CLI/StringTools.hpp"
#include "CLI/TypeTools.hpp"
#include "CLI/Formatter.hpp"
#include "CLI/FormatterFwd.hpp"
namespace CLI {
@ -107,7 +107,7 @@ class App {
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_{AppFormatter()};
std::function<std::string(const App *, std::string, AppFormatMode)> formatter_{Formatter()};
/// The error message printing function INHERITABLE
std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple;

View File

@ -1,185 +0,0 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <string>
#include "CLI/App.hpp"
#include "CLI/Formatter.hpp"
namespace CLI {
inline std::string
AppFormatter::make_group(std::string group, std::vector<const Option *> opts, OptionFormatMode mode) const {
std::stringstream out;
out << "\n" << group << ":\n";
for(const Option *opt : opts) {
out << opt->help(mode);
}
return out.str();
}
inline std::string AppFormatter::make_groups(const App *app, AppFormatMode mode) const {
std::stringstream out;
std::vector<std::string> groups = app->get_groups();
std::vector<const Option *> positionals =
app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
if(!positionals.empty())
out << make_group(get_label("Positionals"), positionals, OptionFormatMode::Positional);
// Options
for(const std::string &group : groups) {
std::vector<const Option *> grouped_items =
app->get_options([&group](const Option *opt) { return opt->nonpositional() && opt->get_group() == group; });
if(mode == AppFormatMode::Sub) {
grouped_items.erase(std::remove_if(grouped_items.begin(),
grouped_items.end(),
[app](const Option *opt) {
return app->get_help_ptr() == opt || app->get_help_all_ptr() == opt;
}),
grouped_items.end());
}
if(!group.empty() && !grouped_items.empty()) {
out << make_group(group, grouped_items, OptionFormatMode::Optional);
if(group != groups.back())
out << "\n";
}
}
return out.str();
}
inline std::string AppFormatter::make_description(const App *app) const {
std::string desc = app->get_description();
if(!desc.empty())
return desc + "\n";
else
return "";
}
inline std::string AppFormatter::make_usage(const App *app, std::string name) const {
std::stringstream out;
out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;
std::vector<std::string> groups = app->get_groups();
// Print an Options badge if any options exist
std::vector<const Option *> non_pos_options =
app->get_options([](const Option *opt) { return opt->nonpositional(); });
if(!non_pos_options.empty())
out << " [" << get_label("OPTIONS") << "]";
// Positionals need to be listed here
std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
// Print out positionals if any are left
if(!positionals.empty()) {
// Convert to help names
std::vector<std::string> positional_names(positionals.size());
std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [](const Option *opt) {
return opt->help(OptionFormatMode::Usage);
});
out << " " << detail::join(positional_names, " ");
}
// Add a marker if subcommands are expected or optional
if(!app->get_subcommands({}).empty()) {
out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "")
<< get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
: "SUBCOMMANDS")
<< (app->get_require_subcommand_min() == 0 ? "]" : "");
}
out << std::endl;
return out.str();
}
inline std::string AppFormatter::make_footer(const App *app) const {
std::string footer = app->get_footer();
if(!footer.empty())
return footer + "\n";
else
return "";
}
inline std::string AppFormatter::operator()(const App *app, std::string name, AppFormatMode mode) const {
std::stringstream out;
if(mode == AppFormatMode::Normal) {
out << make_description(app);
out << make_usage(app, name);
out << make_groups(app, mode);
out << make_subcommands(app, mode);
out << make_footer(app);
} else if(mode == AppFormatMode::Sub) {
out << make_expanded(app);
} else if(mode == AppFormatMode::All) {
out << make_description(app);
out << make_usage(app, name);
out << make_groups(app, mode);
out << make_subcommands(app, mode);
}
return out.str();
}
inline std::string AppFormatter::make_subcommands(const App *app, AppFormatMode mode) const {
std::stringstream out;
std::vector<const App *> subcommands = app->get_subcommands({});
// Make a list in definition order of the groups seen
std::vector<std::string> subcmd_groups_seen;
for(const App *com : subcommands) {
std::string group_key = com->get_group();
if(!group_key.empty() &&
std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
return detail::to_lower(a) == detail::to_lower(group_key);
}) == subcmd_groups_seen.end())
subcmd_groups_seen.push_back(group_key);
}
// For each group, filter out and print subcommands
for(const std::string &group : subcmd_groups_seen) {
out << "\n" << group << ":\n";
if(mode == AppFormatMode::All)
out << "\n";
std::vector<const App *> subcommands_group = app->get_subcommands(
[&group](const App *app) { return detail::to_lower(app->get_group()) == detail::to_lower(group); });
for(const App *new_com : subcommands_group) {
if(mode != AppFormatMode::All) {
out << make_subcommand(new_com);
} else {
out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
if(new_com != subcommands_group.back())
out << "\n";
}
}
}
return out.str();
}
inline std::string AppFormatter::make_subcommand(const App *sub) const {
std::stringstream out;
detail::format_help(out, sub->get_name(), sub->get_description(), column_width_);
return out.str();
}
inline std::string AppFormatter::make_expanded(const App *sub) const {
std::stringstream out;
out << sub->get_name() << "\n " << sub->get_description();
out << make_groups(sub, AppFormatMode::Sub);
return out.str();
}
} // namespace CLI

View File

@ -24,12 +24,10 @@
#include "CLI/Validators.hpp"
#include "CLI/Formatter.hpp"
#include "CLI/FormatterFwd.hpp"
#include "CLI/Option.hpp"
#include "CLI/OptionFormatter.hpp"
#include "CLI/App.hpp"
#include "CLI/AppFormatter.hpp"
#include "CLI/Formatter.hpp"

View File

@ -4,187 +4,234 @@
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <string>
#include <map>
#include "CLI/StringTools.hpp"
#include "CLI/App.hpp"
#include "CLI/Formatter.hpp"
namespace CLI {
class Option;
class App;
/// This enum signifies what situation the option is beig printed in.
///
/// This is passed in as part of the built in formatter in App; it is
/// possible that a custom App formatter could avoid using it, however.
enum class OptionFormatMode {
Usage, //< In the program usage line
Positional, //< In the positionals
Optional //< In the normal optionals
};
/// This enum signifies the type of help requested
///
/// This is passed in by App; all user classes must accept this as
/// the second argument.
enum class AppFormatMode {
Normal, //< The normal, detailed help
All, //< A fully expanded help
Sub, //< Used when printed as part of expanded subcommand
};
/// This is an example formatter (and also the default formatter)
/// For option help.
class OptionFormatter {
protected:
/// @name Options
///@{
/// @brief The required help printout labels (user changeable)
/// Values are REQUIRED, NEEDS, EXCLUDES
std::map<std::string, std::string> labels_{{"REQUIRED", "(REQUIRED)"}};
/// The width of the first column
size_t column_width_{30};
///@}
/// @name Basic
///@{
public:
OptionFormatter() = default;
OptionFormatter(const OptionFormatter &) = default;
OptionFormatter(OptionFormatter &&) = default;
///@}
/// @name Setters
///@{
/// Set the "REQUIRED" label
void label(std::string key, std::string val) { labels_[key] = val; }
/// Set the column width
void column_width(size_t val) { column_width_ = val; }
///@}
/// @name Getters
///@{
/// Get the current value of a name (REQUIRED, etc.)
std::string get_label(std::string key) const {
if(labels_.find(key) == labels_.end())
return key;
else
return labels_.at(key);
inline std::string
Formatter::make_group(std::string group, std::vector<const Option *> opts, bool is_positional) const {
std::stringstream out;
out << "\n" << group << ":\n";
for(const Option *opt : opts) {
out << make_option(opt, is_positional);
}
/// Get the current column width
size_t get_column_width() const { return column_width_; }
///@}
/// @name Overridables
///@{
/// @brief This is the name part of an option, Default: left column
virtual std::string make_name(const Option *, OptionFormatMode) const;
/// @brief This is the options part of the name, Default: combined into left column
virtual std::string make_opts(const Option *) const;
/// @brief This is the description. Default: Right column, on new line if left column too large
virtual std::string make_desc(const Option *) const;
/// @brief This is used to print the name on the USAGE line (by App formatter)
virtual std::string make_usage(const Option *opt) const;
/// @brief This is the standard help combiner that does the "default" thing.
virtual std::string operator()(const Option *opt, OptionFormatMode mode) const {
std::stringstream out;
if(mode == OptionFormatMode::Usage)
out << make_usage(opt);
else
detail::format_help(out, make_name(opt, mode) + make_opts(opt), make_desc(opt), column_width_);
return out.str();
}
///@}
};
inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
std::stringstream out;
std::vector<std::string> groups = app->get_groups();
std::vector<const Option *> positionals =
app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
class AppFormatter {
/// @name Options
///@{
if(!positionals.empty())
out << make_group(get_label("Positionals"), positionals, true);
/// The width of the first column
size_t column_width_{30};
// Options
for(const std::string &group : groups) {
std::vector<const Option *> grouped_items =
app->get_options([&group](const Option *opt) { return opt->nonpositional() && opt->get_group() == group; });
/// @brief The required help printout labels (user changeable)
/// Values are Needs, Excludes, etc.
std::map<std::string, std::string> labels_;
///@}
/// @name Basic
///@{
public:
AppFormatter() = default;
AppFormatter(const AppFormatter &) = default;
AppFormatter(AppFormatter &&) = default;
///@}
/// @name Setters
///@{
/// Set the "REQUIRED" label
void label(std::string key, std::string val) { labels_[key] = val; }
/// Set the column width
void column_width(size_t val) { column_width_ = val; }
///@}
/// @name Getters
///@{
/// Get the current value of a name (REQUIRED, etc.)
std::string get_label(std::string key) const {
if(labels_.find(key) == labels_.end())
return key;
else
return labels_.at(key);
if(mode == AppFormatMode::Sub) {
grouped_items.erase(std::remove_if(grouped_items.begin(),
grouped_items.end(),
[app](const Option *opt) {
return app->get_help_ptr() == opt || app->get_help_all_ptr() == opt;
}),
grouped_items.end());
}
/// Get the current column width
size_t get_column_width() const { return column_width_; }
if(!group.empty() && !grouped_items.empty()) {
out << make_group(group, grouped_items, false);
if(group != groups.back())
out << "\n";
}
}
/// @name Overridables
///@{
return out.str();
}
/// This prints out a group of options
virtual std::string make_group(std::string group, std::vector<const Option *> opts, OptionFormatMode mode) const;
inline std::string Formatter::make_description(const App *app) const {
std::string desc = app->get_description();
/// This prints out all the groups of options
virtual std::string make_groups(const App *app, AppFormatMode mode) const;
if(!desc.empty())
return desc + "\n";
else
return "";
}
/// This prints out all the subcommands
virtual std::string make_subcommands(const App *app, AppFormatMode mode) const;
inline std::string Formatter::make_usage(const App *app, std::string name) const {
std::stringstream out;
/// This prints out a subcommand
virtual std::string make_subcommand(const App *sub) const;
out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;
/// This prints out a subcommand in help-all
virtual std::string make_expanded(const App *sub) const;
std::vector<std::string> groups = app->get_groups();
/// This prints out all the groups of options
virtual std::string make_footer(const App *app) const;
// Print an Options badge if any options exist
std::vector<const Option *> non_pos_options =
app->get_options([](const Option *opt) { return opt->nonpositional(); });
if(!non_pos_options.empty())
out << " [" << get_label("OPTIONS") << "]";
/// This displays the description line
virtual std::string make_description(const App *app) const;
// Positionals need to be listed here
std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
/// This displays the usage line
virtual std::string make_usage(const App *app, std::string name) const;
// Print out positionals if any are left
if(!positionals.empty()) {
// Convert to help names
std::vector<std::string> positional_names(positionals.size());
std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) {
return make_option_usage(opt);
});
/// This puts everything together
virtual std::string operator()(const App *, std::string, AppFormatMode) const;
///@}
};
out << " " << detail::join(positional_names, " ");
}
// Add a marker if subcommands are expected or optional
if(!app->get_subcommands({}).empty()) {
out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "")
<< get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
: "SUBCOMMANDS")
<< (app->get_require_subcommand_min() == 0 ? "]" : "");
}
out << std::endl;
return out.str();
}
inline std::string Formatter::make_footer(const App *app) const {
std::string footer = app->get_footer();
if(!footer.empty())
return footer + "\n";
else
return "";
}
inline std::string Formatter::operator()(const App *app, std::string name, AppFormatMode mode) const {
std::stringstream out;
if(mode == AppFormatMode::Normal) {
out << make_description(app);
out << make_usage(app, name);
out << make_groups(app, mode);
out << make_subcommands(app, mode);
out << make_footer(app);
} else if(mode == AppFormatMode::Sub) {
out << make_expanded(app);
} else if(mode == AppFormatMode::All) {
out << make_description(app);
out << make_usage(app, name);
out << make_groups(app, mode);
out << make_subcommands(app, mode);
}
return out.str();
}
inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
std::stringstream out;
std::vector<const App *> subcommands = app->get_subcommands({});
// Make a list in definition order of the groups seen
std::vector<std::string> subcmd_groups_seen;
for(const App *com : subcommands) {
std::string group_key = com->get_group();
if(!group_key.empty() &&
std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
return detail::to_lower(a) == detail::to_lower(group_key);
}) == subcmd_groups_seen.end())
subcmd_groups_seen.push_back(group_key);
}
// For each group, filter out and print subcommands
for(const std::string &group : subcmd_groups_seen) {
out << "\n" << group << ":\n";
if(mode == AppFormatMode::All)
out << "\n";
std::vector<const App *> subcommands_group = app->get_subcommands(
[&group](const App *app) { return detail::to_lower(app->get_group()) == detail::to_lower(group); });
for(const App *new_com : subcommands_group) {
if(mode != AppFormatMode::All) {
out << make_subcommand(new_com);
} else {
out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
if(new_com != subcommands_group.back())
out << "\n";
}
}
}
return out.str();
}
inline std::string Formatter::make_subcommand(const App *sub) const {
std::stringstream out;
detail::format_help(out, sub->get_name(), sub->get_description(), column_width_);
return out.str();
}
inline std::string Formatter::make_expanded(const App *sub) const {
std::stringstream out;
out << sub->get_name() << "\n " << sub->get_description();
out << make_groups(sub, AppFormatMode::Sub);
return out.str();
}
inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
if(is_positional)
return opt->get_name(true, false);
else
return opt->get_name(false, true);
}
inline std::string Formatter::make_option_opts(const Option *opt) const {
std::stringstream out;
if(opt->get_type_size() != 0) {
if(!opt->get_typeval().empty())
out << " " << get_label(opt->get_typeval());
if(!opt->get_defaultval().empty())
out << "=" << opt->get_defaultval();
if(opt->get_expected() > 1)
out << " x " << opt->get_expected();
if(opt->get_expected() == -1)
out << " ...";
if(opt->get_required())
out << " " << get_label("REQUIRED");
}
if(!opt->get_envname().empty())
out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
if(!opt->get_needs().empty()) {
out << " " << get_label("Needs") << ":";
for(const Option *op : opt->get_needs())
out << " " << op->get_name();
}
if(!opt->get_excludes().empty()) {
out << " " << get_label("Excludes") << ":";
for(const Option *op : opt->get_excludes())
out << " " << op->get_name();
}
return out.str();
}
inline std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
inline std::string Formatter::make_option_usage(const Option *opt) const {
// Note that these are positionals usages
std::stringstream out;
out << make_option_name(opt, true);
if(opt->get_expected() > 1)
out << "(" << std::to_string(opt->get_expected()) << "x)";
else if(opt->get_expected() < 0)
out << "...";
return opt->get_required() ? out.str() : "[" + out.str() + "]";
}
} // namespace CLI

View File

@ -0,0 +1,127 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <string>
#include <map>
#include "CLI/StringTools.hpp"
namespace CLI {
class Option;
class App;
/// This enum signifies the type of help requested
///
/// This is passed in by App; all user classes must accept this as
/// the second argument.
enum class AppFormatMode {
Normal, //< The normal, detailed help
All, //< A fully expanded help
Sub, //< Used when printed as part of expanded subcommand
};
class Formatter {
/// @name Options
///@{
/// The width of the first column
size_t column_width_{30};
/// @brief The required help printout labels (user changeable)
/// Values are Needs, Excludes, etc.
std::map<std::string, std::string> labels_;
///@}
/// @name Basic
///@{
public:
Formatter() = default;
Formatter(const Formatter &) = default;
Formatter(Formatter &&) = default;
///@}
/// @name Setters
///@{
/// Set the "REQUIRED" label
void label(std::string key, std::string val) { labels_[key] = val; }
/// Set the column width
void column_width(size_t val) { column_width_ = val; }
///@}
/// @name Getters
///@{
/// Get the current value of a name (REQUIRED, etc.)
std::string get_label(std::string key) const {
if(labels_.find(key) == labels_.end())
return key;
else
return labels_.at(key);
}
/// Get the current column width
size_t get_column_width() const { return column_width_; }
/// @name Overridables
///@{
/// This prints out a group of options
virtual std::string make_group(std::string group, std::vector<const Option *> opts, bool is_positional) const;
/// This prints out all the groups of options
virtual std::string make_groups(const App *app, AppFormatMode mode) const;
/// This prints out all the subcommands
virtual std::string make_subcommands(const App *app, AppFormatMode mode) const;
/// This prints out a subcommand
virtual std::string make_subcommand(const App *sub) const;
/// This prints out a subcommand in help-all
virtual std::string make_expanded(const App *sub) const;
/// This prints out all the groups of options
virtual std::string make_footer(const App *app) const;
/// This displays the description line
virtual std::string make_description(const App *app) const;
/// This displays the usage line
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;
///@}
/// @name Options
///@{
virtual std::string make_option(const Option *opt, bool is_positional) const {
std::stringstream out;
detail::format_help(
out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_);
return out.str();
}
/// @brief This is the name part of an option, Default: left column
virtual std::string make_option_name(const Option *, bool) const;
/// @brief This is the options part of the name, Default: combined into left column
virtual std::string make_option_opts(const Option *) const;
/// @brief This is the description. Default: Right column, on new line if left column too large
virtual std::string make_option_desc(const Option *) const;
/// @brief This is used to print the name on the USAGE line
virtual std::string make_option_usage(const Option *opt) const;
///@}
};
} // namespace CLI

View File

@ -16,7 +16,6 @@
#include "CLI/Macros.hpp"
#include "CLI/Split.hpp"
#include "CLI/StringTools.hpp"
#include "CLI/Formatter.hpp"
namespace CLI {
@ -49,17 +48,12 @@ template <typename CRTP> class OptionBase {
/// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too)
MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
/// @brief A formatter to print out help
/// Used by the default App help formatter.
std::function<std::string(const Option *, OptionFormatMode)> formatter_{OptionFormatter()};
template <typename T> void copy_to(T *other) const {
other->group(group_);
other->required(required_);
other->ignore_case(ignore_case_);
other->configurable(configurable_);
other->multi_option_policy(multi_option_policy_);
other->formatter(formatter_);
}
public:
@ -81,12 +75,6 @@ template <typename CRTP> class OptionBase {
/// Support Plumbum term
CRTP *mandatory(bool value = true) { return required(value); }
/// Set a formatter for this option
CRTP *formatter(std::function<std::string(const Option *, OptionFormatMode)> value) {
formatter_ = value;
return static_cast<CRTP *>(this);
}
// Getters
/// Get the group of this option
@ -491,17 +479,6 @@ class Option : public OptionBase<Option> {
}
}
/// \brief Call this with a OptionFormatMode to run the currently configured help formatter.
///
/// Changed in Version 1.6:
///
/// * `help_positinoal` MOVED TO `help_usage` (name not included) or Usage mode
/// * `help_name` CHANGED to `help_name` with different true/false flags
/// * `pname` with type info MOVED to `help_name`
/// * `help_aftername()` MOVED to `help_opts()`
/// * Instead of `opt->help_mode()` use `opt->help(mode)`
std::string help(OptionFormatMode mode) const { return formatter_(this, mode); }
///@}
/// @name Parser tools
///@{

View File

@ -1,65 +0,0 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <string>
#include "CLI/Option.hpp"
#include "CLI/Formatter.hpp"
namespace CLI {
inline std::string OptionFormatter::make_name(const Option *opt, OptionFormatMode mode) const {
if(mode == OptionFormatMode::Optional)
return opt->get_name(false, true);
else
return opt->get_name(true, false);
}
inline std::string OptionFormatter::make_opts(const Option *opt) const {
std::stringstream out;
if(opt->get_type_size() != 0) {
if(!opt->get_typeval().empty())
out << " " << get_label(opt->get_typeval());
if(!opt->get_defaultval().empty())
out << "=" << opt->get_defaultval();
if(opt->get_expected() > 1)
out << " x " << opt->get_expected();
if(opt->get_expected() == -1)
out << " ...";
if(opt->get_required())
out << " " << get_label("REQUIRED");
}
if(!opt->get_envname().empty())
out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
if(!opt->get_needs().empty()) {
out << " " << get_label("Needs") << ":";
for(const Option *op : opt->get_needs())
out << " " << op->get_name();
}
if(!opt->get_excludes().empty()) {
out << " " << get_label("Excludes") << ":";
for(const Option *op : opt->get_excludes())
out << " " << op->get_name();
}
return out.str();
}
inline std::string OptionFormatter::make_desc(const Option *opt) const { return opt->get_description(); }
inline std::string OptionFormatter::make_usage(const Option *opt) const {
// Note that these are positionals usages
std::stringstream out;
out << make_name(opt, OptionFormatMode::Usage);
if(opt->get_expected() > 1)
out << "(" << std::to_string(opt->get_expected()) << "x)";
else if(opt->get_expected() < 0)
out << "...";
return opt->get_required() ? out.str() : "[" + out.str() + "]";
}
} // namespace CLI

View File

@ -25,10 +25,10 @@ TEST(Formatter, Nothing) {
TEST(Formatter, OptCustomize) {
CLI::App app{"My prog"};
CLI::OptionFormatter optfmt;
CLI::Formatter optfmt;
optfmt.column_width(25);
optfmt.label("REQUIRED", "(MUST HAVE)");
app.option_defaults()->formatter(optfmt);
app.formatter(optfmt);
int v;
app.add_option("--opt", v, "Something")->required();
@ -48,7 +48,7 @@ TEST(Formatter, AptCustomize) {
CLI::App app{"My prog"};
app.add_subcommand("subcom1", "This");
CLI::AppFormatter appfmt;
CLI::Formatter appfmt;
appfmt.column_width(20);
appfmt.label("Usage", "Run");
app.formatter(appfmt);

View File

@ -570,7 +570,7 @@ TEST(THelp, RequiredPrintout) {
int x;
app.add_option("-a,--alpha", x)->required();
EXPECT_THAT(app.help(), HasSubstr("(REQUIRED)"));
EXPECT_THAT(app.help(), HasSubstr(" REQUIRED"));
}
TEST(THelp, GroupOrder) {