CLI11
C++11 Command Line Interface Parser
Loading...
Searching...
No Matches
Formatter_inl.hpp
1// Copyright (c) 2017-2024, University of Cincinnati, developed by Henry Schreiner
2// under NSF AWARD 1414736 and by the respective contributors.
3// All rights reserved.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6
7#pragma once
8
9// IWYU pragma: private, include "CLI/CLI.hpp"
10
11// This include is only needed for IDEs to discover symbols
12#include "../Formatter.hpp"
13
14// [CLI11:public_includes:set]
15#include <algorithm>
16#include <string>
17#include <utility>
18#include <vector>
19// [CLI11:public_includes:end]
20
21namespace CLI {
22// [CLI11:formatter_inl_hpp:verbatim]
23CLI11_INLINE std::string
24Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const {
25 std::stringstream out;
26
27 out << "\n" << group << ":\n";
28 for(const Option *opt : opts) {
29 out << make_option(opt, is_positional);
30 }
31
32 return out.str();
33}
34
35CLI11_INLINE std::string Formatter::make_positionals(const App *app) const {
36 std::vector<const Option *> opts =
37 app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
38
39 if(opts.empty())
40 return {};
41
42 return make_group(get_label("Positionals"), true, opts);
43}
44
45CLI11_INLINE std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
46 std::stringstream out;
47 std::vector<std::string> groups = app->get_groups();
48
49 // Options
50 for(const std::string &group : groups) {
51 std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) {
52 return opt->get_group() == group // Must be in the right group
53 && opt->nonpositional() // Must not be a positional
54 && (mode != AppFormatMode::Sub // If mode is Sub, then
55 || (app->get_help_ptr() != opt // Ignore help pointer
56 && app->get_help_all_ptr() != opt)); // Ignore help all pointer
57 });
58 if(!group.empty() && !opts.empty()) {
59 out << make_group(group, false, opts);
60
61 if(group != groups.back())
62 out << "\n";
63 }
64 }
65
66 return out.str();
67}
68
69CLI11_INLINE std::string Formatter::make_description(const App *app) const {
70 std::string desc = app->get_description();
71 auto min_options = app->get_require_option_min();
72 auto max_options = app->get_require_option_max();
73 if(app->get_required()) {
74 desc += " " + get_label("REQUIRED") + " ";
75 }
76 if((max_options == min_options) && (min_options > 0)) {
77 if(min_options == 1) {
78 desc += " \n[Exactly 1 of the following options is required]";
79 } else {
80 desc += " \n[Exactly " + std::to_string(min_options) + " options from the following list are required]";
81 }
82 } else if(max_options > 0) {
83 if(min_options > 0) {
84 desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) +
85 " of the follow options are required]";
86 } else {
87 desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]";
88 }
89 } else if(min_options > 0) {
90 desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]";
91 }
92 return (!desc.empty()) ? desc + "\n" : std::string{};
93}
94
95CLI11_INLINE std::string Formatter::make_usage(const App *app, std::string name) const {
96 std::string usage = app->get_usage();
97 if(!usage.empty()) {
98 return usage + "\n";
99 }
100
101 std::stringstream out;
102
103 out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;
104
105 std::vector<std::string> groups = app->get_groups();
106
107 // Print an Options badge if any options exist
108 std::vector<const Option *> non_pos_options =
109 app->get_options([](const Option *opt) { return opt->nonpositional(); });
110 if(!non_pos_options.empty())
111 out << " [" << get_label("OPTIONS") << "]";
112
113 // Positionals need to be listed here
114 std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
115
116 // Print out positionals if any are left
117 if(!positionals.empty()) {
118 // Convert to help names
119 std::vector<std::string> positional_names(positionals.size());
120 std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) {
121 return make_option_usage(opt);
122 });
123
124 out << " " << detail::join(positional_names, " ");
125 }
126
127 // Add a marker if subcommands are expected or optional
128 if(!app->get_subcommands(
129 [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); })
130 .empty()) {
131 out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "")
132 << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
133 : "SUBCOMMANDS")
134 << (app->get_require_subcommand_min() == 0 ? "]" : "");
135 }
136
137 out << '\n';
138
139 return out.str();
140}
141
142CLI11_INLINE std::string Formatter::make_footer(const App *app) const {
143 std::string footer = app->get_footer();
144 if(footer.empty()) {
145 return std::string{};
146 }
147 return "\n" + footer + "\n";
148}
149
150CLI11_INLINE std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
151
152 // This immediately forwards to the make_expanded method. This is done this way so that subcommands can
153 // have overridden formatters
154 if(mode == AppFormatMode::Sub)
155 return make_expanded(app);
156
157 std::stringstream out;
158 if((app->get_name().empty()) && (app->get_parent() != nullptr)) {
159 if(app->get_group() != "Subcommands") {
160 out << app->get_group() << ':';
161 }
162 }
163
164 out << make_description(app);
165 out << make_usage(app, name);
166 out << make_positionals(app);
167 out << make_groups(app, mode);
168 out << make_subcommands(app, mode);
169 out << make_footer(app);
170
171 return out.str();
172}
173
174CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
175 std::stringstream out;
176
177 std::vector<const App *> subcommands = app->get_subcommands({});
178
179 // Make a list in definition order of the groups seen
180 std::vector<std::string> subcmd_groups_seen;
181 for(const App *com : subcommands) {
182 if(com->get_name().empty()) {
183 if(!com->get_group().empty()) {
184 out << make_expanded(com);
185 }
186 continue;
187 }
188 std::string group_key = com->get_group();
189 if(!group_key.empty() &&
190 std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
191 return detail::to_lower(a) == detail::to_lower(group_key);
192 }) == subcmd_groups_seen.end())
193 subcmd_groups_seen.push_back(group_key);
194 }
195
196 // For each group, filter out and print subcommands
197 for(const std::string &group : subcmd_groups_seen) {
198 out << "\n" << group << ":\n";
199 std::vector<const App *> subcommands_group = app->get_subcommands(
200 [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); });
201 for(const App *new_com : subcommands_group) {
202 if(new_com->get_name().empty())
203 continue;
204 if(mode != AppFormatMode::All) {
205 out << make_subcommand(new_com);
206 } else {
207 out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
208 out << "\n";
209 }
210 }
211 }
212
213 return out.str();
214}
215
216CLI11_INLINE std::string Formatter::make_subcommand(const App *sub) const {
217 std::stringstream out;
218 detail::format_help(out,
219 sub->get_display_name(true) + (sub->get_required() ? " " + get_label("REQUIRED") : ""),
220 sub->get_description(),
222 return out.str();
223}
224
225CLI11_INLINE std::string Formatter::make_expanded(const App *sub) const {
226 std::stringstream out;
227 out << sub->get_display_name(true) << "\n";
228
229 out << make_description(sub);
230 if(sub->get_name().empty() && !sub->get_aliases().empty()) {
231 detail::format_aliases(out, sub->get_aliases(), column_width_ + 2);
232 }
233 out << make_positionals(sub);
234 out << make_groups(sub, AppFormatMode::Sub);
235 out << make_subcommands(sub, AppFormatMode::Sub);
236
237 // Drop blank spaces
238 std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n");
239 tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n'
240
241 // Indent all but the first line (the name)
242 return detail::find_and_replace(tmp, "\n", "\n ") + "\n";
243}
244
245CLI11_INLINE std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
246 if(is_positional)
247 return opt->get_name(true, false);
248
249 return opt->get_name(false, true);
250}
251
252CLI11_INLINE std::string Formatter::make_option_opts(const Option *opt) const {
253 std::stringstream out;
254
255 if(!opt->get_option_text().empty()) {
256 out << " " << opt->get_option_text();
257 } else {
258 if(opt->get_type_size() != 0) {
259 if(!opt->get_type_name().empty())
260 out << " " << get_label(opt->get_type_name());
261 if(!opt->get_default_str().empty())
262 out << " [" << opt->get_default_str() << "] ";
263 if(opt->get_expected_max() == detail::expected_max_vector_size)
264 out << " ...";
265 else if(opt->get_expected_min() > 1)
266 out << " x " << opt->get_expected();
267
268 if(opt->get_required())
269 out << " " << get_label("REQUIRED");
270 }
271 if(!opt->get_envname().empty())
272 out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
273 if(!opt->get_needs().empty()) {
274 out << " " << get_label("Needs") << ":";
275 for(const Option *op : opt->get_needs())
276 out << " " << op->get_name();
277 }
278 if(!opt->get_excludes().empty()) {
279 out << " " << get_label("Excludes") << ":";
280 for(const Option *op : opt->get_excludes())
281 out << " " << op->get_name();
282 }
283 }
284 return out.str();
285}
286
287CLI11_INLINE std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
288
289CLI11_INLINE std::string Formatter::make_option_usage(const Option *opt) const {
290 // Note that these are positionals usages
291 std::stringstream out;
292 out << make_option_name(opt, true);
293 if(opt->get_expected_max() >= detail::expected_max_vector_size)
294 out << "...";
295 else if(opt->get_expected_max() > 1)
296 out << "(" << opt->get_expected() << "x)";
297
298 return opt->get_required() ? out.str() : "[" + out.str() + "]";
299}
300// [CLI11:formatter_inl_hpp:end]
301} // namespace CLI
Creates a command line program, with very few defaults.
Definition App.hpp:90
std::size_t column_width_
The width of the first column.
Definition FormatterFwd.hpp:48
CLI11_NODISCARD std::string get_label(std::string key) const
Get the current value of a name (REQUIRED, etc.)
Definition FormatterFwd.hpp:86
std::string make_help(const App *, std::string, AppFormatMode) const override
This puts everything together.
Definition Formatter_inl.hpp:150
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:168
std::string make_groups(const App *app, AppFormatMode mode) const
This prints out all the groups of options.
Definition Formatter_inl.hpp:45
virtual std::string make_usage(const App *app, std::string name) const
This displays the usage line.
Definition Formatter_inl.hpp:95
virtual std::string make_option_name(const Option *, bool) const
This is the name part of an option, Default: left column.
Definition Formatter_inl.hpp:245
virtual CLI11_NODISCARD std::string make_group(std::string group, bool is_positional, std::vector< const Option * > opts) const
Definition Formatter_inl.hpp:24
virtual std::string make_footer(const App *app) const
This prints out all the groups of options.
Definition Formatter_inl.hpp:142
virtual std::string make_option_usage(const Option *opt) const
This is used to print the name on the USAGE line.
Definition Formatter_inl.hpp:289
virtual std::string make_subcommand(const App *sub) const
This prints out a subcommand.
Definition Formatter_inl.hpp:216
virtual std::string make_positionals(const App *app) const
This prints out just the positionals "group".
Definition Formatter_inl.hpp:35
virtual std::string make_subcommands(const App *app, AppFormatMode mode) const
This prints out all the subcommands.
Definition Formatter_inl.hpp:174
virtual std::string make_description(const App *app) const
This displays the description line.
Definition Formatter_inl.hpp:69
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_inl.hpp:287
virtual std::string make_option_opts(const Option *) const
This is the options part of the name, Default: combined into left column.
Definition Formatter_inl.hpp:252
virtual std::string make_expanded(const App *sub) const
This prints out a subcommand in help-all.
Definition Formatter_inl.hpp:225
Definition Option.hpp:231