CLI11
C++11 Command Line Interface Parser
Formatter.hpp
1 #pragma once
2 
3 // Distributed under the 3-Clause BSD License. See accompanying
4 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
5 
6 #include <string>
7 
8 #include "CLI/App.hpp"
9 #include "CLI/FormatterFwd.hpp"
10 
11 namespace CLI {
12 
13 inline std::string
14 Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const {
15  std::stringstream out;
16 
17  out << "\n" << group << ":\n";
18  for(const Option *opt : opts) {
19  out << make_option(opt, is_positional);
20  }
21 
22  return out.str();
23 }
24 
25 inline std::string Formatter::make_positionals(const App *app) const {
26  std::vector<const Option *> opts =
27  app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
28 
29  if(opts.empty())
30  return std::string();
31  else
32  return make_group(get_label("Positionals"), true, opts);
33 }
34 
35 inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
36  std::stringstream out;
37  std::vector<std::string> groups = app->get_groups();
38 
39  // Options
40  for(const std::string &group : groups) {
41  std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) {
42  return opt->get_group() == group // Must be in the right group
43  && opt->nonpositional() // Must not be a positional
44  && (mode != AppFormatMode::Sub // If mode is Sub, then
45  || (app->get_help_ptr() != opt // Ignore help pointer
46  && app->get_help_all_ptr() != opt)); // Ignore help all pointer
47  });
48  if(!group.empty() && !opts.empty()) {
49  out << make_group(group, false, opts);
50 
51  if(group != groups.back())
52  out << "\n";
53  }
54  }
55 
56  return out.str();
57 }
58 
59 inline std::string Formatter::make_description(const App *app) const {
60  std::string desc = app->get_description();
61  auto min_options = app->get_require_option_min();
62  auto max_options = app->get_require_option_max();
63  if(app->get_required()) {
64  desc += " REQUIRED ";
65  }
66  if((max_options == min_options) && (min_options > 0)) {
67  if(min_options == 1) {
68  desc += " \n[Exactly 1 of the following options is required]";
69  } else {
70  desc += " \n[Exactly " + std::to_string(min_options) + "options from the following list are required]";
71  }
72  } else if(max_options > 0) {
73  if(min_options > 0) {
74  desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) +
75  " of the follow options are required]";
76  } else {
77  desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]";
78  }
79  } else if(min_options > 0) {
80  desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]";
81  }
82  return (!desc.empty()) ? desc + "\n" : std::string{};
83 }
84 
85 inline std::string Formatter::make_usage(const App *app, std::string name) const {
86  std::stringstream out;
87 
88  out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;
89 
90  std::vector<std::string> groups = app->get_groups();
91 
92  // Print an Options badge if any options exist
93  std::vector<const Option *> non_pos_options =
94  app->get_options([](const Option *opt) { return opt->nonpositional(); });
95  if(!non_pos_options.empty())
96  out << " [" << get_label("OPTIONS") << "]";
97 
98  // Positionals need to be listed here
99  std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
100 
101  // Print out positionals if any are left
102  if(!positionals.empty()) {
103  // Convert to help names
104  std::vector<std::string> positional_names(positionals.size());
105  std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) {
106  return make_option_usage(opt);
107  });
108 
109  out << " " << detail::join(positional_names, " ");
110  }
111 
112  // Add a marker if subcommands are expected or optional
113  if(!app->get_subcommands(
114  [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); })
115  .empty()) {
116  out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "")
117  << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
118  : "SUBCOMMANDS")
119  << (app->get_require_subcommand_min() == 0 ? "]" : "");
120  }
121 
122  out << std::endl;
123 
124  return out.str();
125 }
126 
127 inline std::string Formatter::make_footer(const App *app) const {
128  std::string footer = app->get_footer();
129  if(!footer.empty())
130  return footer + "\n";
131  else
132  return "";
133 }
134 
135 inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
136 
137  // This immediately forwards to the make_expanded method. This is done this way so that subcommands can
138  // have overridden formatters
139  if(mode == AppFormatMode::Sub)
140  return make_expanded(app);
141 
142  std::stringstream out;
143  if((app->get_name().empty()) && (app->get_parent() != nullptr)) {
144  if(app->get_group() != "Subcommands") {
145  out << app->get_group() << ':';
146  }
147  }
148 
149  out << make_description(app);
150  out << make_usage(app, name);
151  out << make_positionals(app);
152  out << make_groups(app, mode);
153  out << make_subcommands(app, mode);
154  out << make_footer(app);
155 
156  return out.str();
157 }
158 
159 inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
160  std::stringstream out;
161 
162  std::vector<const App *> subcommands = app->get_subcommands({});
163 
164  // Make a list in definition order of the groups seen
165  std::vector<std::string> subcmd_groups_seen;
166  for(const App *com : subcommands) {
167  if(com->get_name().empty()) {
168  out << make_expanded(com);
169  continue;
170  }
171  std::string group_key = com->get_group();
172  if(!group_key.empty() &&
173  std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
174  return detail::to_lower(a) == detail::to_lower(group_key);
175  }) == subcmd_groups_seen.end())
176  subcmd_groups_seen.push_back(group_key);
177  }
178 
179  // For each group, filter out and print subcommands
180  for(const std::string &group : subcmd_groups_seen) {
181  out << "\n" << group << ":\n";
182  std::vector<const App *> subcommands_group = app->get_subcommands(
183  [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); });
184  for(const App *new_com : subcommands_group) {
185  if(new_com->get_name().empty())
186  continue;
187  if(mode != AppFormatMode::All) {
188  out << make_subcommand(new_com);
189  } else {
190  out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
191  out << "\n";
192  }
193  }
194  }
195 
196  return out.str();
197 }
198 
199 inline std::string Formatter::make_subcommand(const App *sub) const {
200  std::stringstream out;
201  detail::format_help(out, sub->get_name(), sub->get_description(), column_width_);
202  return out.str();
203 }
204 
205 inline std::string Formatter::make_expanded(const App *sub) const {
206  std::stringstream out;
207  out << sub->get_display_name() << "\n";
208 
209  out << make_description(sub);
210  out << make_positionals(sub);
211  out << make_groups(sub, AppFormatMode::Sub);
212  out << make_subcommands(sub, AppFormatMode::Sub);
213 
214  // Drop blank spaces
215  std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n");
216  tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n'
217 
218  // Indent all but the first line (the name)
219  return detail::find_and_replace(tmp, "\n", "\n ") + "\n";
220 }
221 
222 inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
223  if(is_positional)
224  return opt->get_name(true, false);
225  else
226  return opt->get_name(false, true);
227 }
228 
229 inline std::string Formatter::make_option_opts(const Option *opt) const {
230  std::stringstream out;
231 
232  if(opt->get_type_size() != 0) {
233  if(!opt->get_type_name().empty())
234  out << " " << get_label(opt->get_type_name());
235  if(!opt->get_default_str().empty())
236  out << "=" << opt->get_default_str();
237  if(opt->get_expected() > 1)
238  out << " x " << opt->get_expected();
239  if(opt->get_expected() == -1)
240  out << " ...";
241  if(opt->get_required())
242  out << " " << get_label("REQUIRED");
243  }
244  if(!opt->get_envname().empty())
245  out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
246  if(!opt->get_needs().empty()) {
247  out << " " << get_label("Needs") << ":";
248  for(const Option *op : opt->get_needs())
249  out << " " << op->get_name();
250  }
251  if(!opt->get_excludes().empty()) {
252  out << " " << get_label("Excludes") << ":";
253  for(const Option *op : opt->get_excludes())
254  out << " " << op->get_name();
255  }
256  return out.str();
257 }
258 
259 inline std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
260 
261 inline std::string Formatter::make_option_usage(const Option *opt) const {
262  // Note that these are positionals usages
263  std::stringstream out;
264  out << make_option_name(opt, true);
265 
266  if(opt->get_expected() > 1)
267  out << "(" << std::to_string(opt->get_expected()) << "x)";
268  else if(opt->get_expected() < 0)
269  out << "...";
270  return opt->get_required() ? out.str() : "[" + out.str() + "]";
271 }
272 
273 } // namespace CLI
int get_type_size() const
The number of arguments the option expects.
Definition: Option.hpp:540
virtual std::string make_option_usage(const Option *opt) const
This is used to print the name on the USAGE line.
Definition: Formatter.hpp:261
std::string get_type_name() const
Get the full typename for this option.
Definition: Option.hpp:969
virtual std::string make_option_desc(const Option *) const
This is the description. Default: Right column, on new line if left column too large.
Definition: Formatter.hpp:259
size_t get_require_subcommand_min() const
Get the required min subcommand value.
Definition: App.hpp:1649
virtual std::string make_option(const Option *opt, bool is_positional) const
This prints out an option help line, either positional or optional form.
Definition: FormatterFwd.hpp:154
virtual std::string make_subcommands(const App *app, AppFormatMode mode) const
This prints out all the subcommands.
Definition: Formatter.hpp:159
std::set< Option * > get_needs() const
The set of options needed.
Definition: Option.hpp:546
std::string make_groups(const App *app, AppFormatMode mode) const
This prints out all the groups of options.
Definition: Formatter.hpp:35
bool get_positional() const
True if the argument can be given directly.
Definition: Option.hpp:595
bool nonpositional() const
True if option has at least one non-positional name.
Definition: Option.hpp:598
size_t get_require_option_min() const
Get the required min option value.
Definition: App.hpp:1655
int get_expected() const
The number of times the option expects to be included.
Definition: Option.hpp:571
const std::string & get_group() const
Get the group of this option.
Definition: Option.hpp:105
const std::string & get_group() const
Get the group of this subcommand.
Definition: App.hpp:1643
size_t get_require_option_max() const
Get the required max option value.
Definition: App.hpp:1658
App * get_parent()
Get the parent of this subcommand (or nullptr if master app)
Definition: App.hpp:1702
std::string get_name() const
Get the name of the current app.
Definition: App.hpp:1708
virtual std::string make_option_opts(const Option *) const
This is the options part of the name, Default: combined into left column.
Definition: Formatter.hpp:229
std::set< Option * > get_excludes() const
The set of options excluded.
Definition: Option.hpp:549
std::vector< App * > get_subcommands() const
Definition: App.hpp:1405
const std::string & get_footer() const
Get footer.
Definition: App.hpp:1646
virtual std::string make_description(const App *app) const
This displays the description line.
Definition: Formatter.hpp:59
virtual std::string make_option_name(const Option *, bool) const
This is the name part of an option, Default: left column.
Definition: Formatter.hpp:222
virtual std::string make_footer(const App *app) const
This prints out all the groups of options.
Definition: Formatter.hpp:127
std::string get_default_str() const
The default value (for help printing)
Definition: Option.hpp:556
Creates a command line program, with very few defaults.
Definition: App.hpp:59
bool get_required() const
True if this is a required option.
Definition: Option.hpp:108
std::string get_label(std::string key) const
Get the current value of a name (REQUIRED, etc.)
Definition: FormatterFwd.hpp:74
virtual std::string make_positionals(const App *app) const
This prints out just the positionals "group".
Definition: Formatter.hpp:25
virtual std::string make_expanded(const App *sub) const
This prints out a subcommand in help-all.
Definition: Formatter.hpp:205
std::vector< const Option * > get_options(const std::function< bool(const Option *)> filter={}) const
Get the list of options (user facing function, so returns raw pointers), has optional filter function...
Definition: App.hpp:1549
Option * get_help_ptr()
Get a pointer to the help flag.
Definition: App.hpp:1687
size_t column_width_
The width of the first column.
Definition: FormatterFwd.hpp:38
std::string get_description() const
Get the app or subcommand description.
Definition: App.hpp:1540
size_t get_require_subcommand_max() const
Get the required max subcommand value.
Definition: App.hpp:1652
std::string get_display_name() const
Get a display name for an app.
Definition: App.hpp:1711
const Option * get_help_all_ptr() const
Get a pointer to the help all flag. (const)
Definition: App.hpp:1693
bool get_required() const
Get the status of required.
Definition: App.hpp:1667
std::string make_help(const App *, std::string, AppFormatMode) const override
This puts everything together.
Definition: Formatter.hpp:135
virtual std::string make_usage(const App *app, std::string name) const
This displays the usage line.
Definition: Formatter.hpp:85
const std::string & get_description() const
Get the description.
Definition: Option.hpp:604
std::string get_name(bool positional=false, bool all_options=false) const
Gets a comma separated list of names. Will include / prefer the positional name if positional is true...
Definition: Option.hpp:620
Definition: Option.hpp:206
virtual std::string make_subcommand(const App *sub) const
This prints out a subcommand.
Definition: Formatter.hpp:199
virtual std::string make_group(std::string group, bool is_positional, std::vector< const Option * > opts) const
Definition: Formatter.hpp:14
std::string get_envname() const
The environment variable associated to this value.
Definition: Option.hpp:543
std::vector< std::string > get_groups() const
Get the groups available directly from this option (in order)
Definition: App.hpp:1729