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