File indexing completed on 2025-01-18 09:54:43
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009
0010 #include <CLI/Formatter.hpp>
0011
0012
0013 #include <algorithm>
0014 #include <string>
0015 #include <utility>
0016 #include <vector>
0017
0018
0019 namespace CLI {
0020
0021 CLI11_INLINE std::string
0022 Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const {
0023 std::stringstream out;
0024
0025 out << "\n" << group << ":\n";
0026 for(const Option *opt : opts) {
0027 out << make_option(opt, is_positional);
0028 }
0029
0030 return out.str();
0031 }
0032
0033 CLI11_INLINE std::string Formatter::make_positionals(const App *app) const {
0034 std::vector<const Option *> opts =
0035 app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
0036
0037 if(opts.empty())
0038 return {};
0039
0040 return make_group(get_label("Positionals"), true, opts);
0041 }
0042
0043 CLI11_INLINE std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
0044 std::stringstream out;
0045 std::vector<std::string> groups = app->get_groups();
0046
0047
0048 for(const std::string &group : groups) {
0049 std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) {
0050 return opt->get_group() == group
0051 && opt->nonpositional()
0052 && (mode != AppFormatMode::Sub
0053 || (app->get_help_ptr() != opt
0054 && app->get_help_all_ptr() != opt));
0055 });
0056 if(!group.empty() && !opts.empty()) {
0057 out << make_group(group, false, opts);
0058
0059 if(group != groups.back())
0060 out << "\n";
0061 }
0062 }
0063
0064 return out.str();
0065 }
0066
0067 CLI11_INLINE std::string Formatter::make_description(const App *app) const {
0068 std::string desc = app->get_description();
0069 auto min_options = app->get_require_option_min();
0070 auto max_options = app->get_require_option_max();
0071 if(app->get_required()) {
0072 desc += " REQUIRED ";
0073 }
0074 if((max_options == min_options) && (min_options > 0)) {
0075 if(min_options == 1) {
0076 desc += " \n[Exactly 1 of the following options is required]";
0077 } else {
0078 desc += " \n[Exactly " + std::to_string(min_options) + " options from the following list are required]";
0079 }
0080 } else if(max_options > 0) {
0081 if(min_options > 0) {
0082 desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) +
0083 " of the follow options are required]";
0084 } else {
0085 desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]";
0086 }
0087 } else if(min_options > 0) {
0088 desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]";
0089 }
0090 return (!desc.empty()) ? desc + "\n" : std::string{};
0091 }
0092
0093 CLI11_INLINE std::string Formatter::make_usage(const App *app, std::string name) const {
0094 std::stringstream out;
0095
0096 out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;
0097
0098 std::vector<std::string> groups = app->get_groups();
0099
0100
0101 std::vector<const Option *> non_pos_options =
0102 app->get_options([](const Option *opt) { return opt->nonpositional(); });
0103 if(!non_pos_options.empty())
0104 out << " [" << get_label("OPTIONS") << "]";
0105
0106
0107 std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
0108
0109
0110 if(!positionals.empty()) {
0111
0112 std::vector<std::string> positional_names(positionals.size());
0113 std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) {
0114 return make_option_usage(opt);
0115 });
0116
0117 out << " " << detail::join(positional_names, " ");
0118 }
0119
0120
0121 if(!app->get_subcommands(
0122 [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); })
0123 .empty()) {
0124 out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "")
0125 << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
0126 : "SUBCOMMANDS")
0127 << (app->get_require_subcommand_min() == 0 ? "]" : "");
0128 }
0129
0130 out << std::endl;
0131
0132 return out.str();
0133 }
0134
0135 CLI11_INLINE std::string Formatter::make_footer(const App *app) const {
0136 std::string footer = app->get_footer();
0137 if(footer.empty()) {
0138 return std::string{};
0139 }
0140 return "\n" + footer + "\n";
0141 }
0142
0143 CLI11_INLINE std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
0144
0145
0146
0147 if(mode == AppFormatMode::Sub)
0148 return make_expanded(app);
0149
0150 std::stringstream out;
0151 if((app->get_name().empty()) && (app->get_parent() != nullptr)) {
0152 if(app->get_group() != "Subcommands") {
0153 out << app->get_group() << ':';
0154 }
0155 }
0156
0157 out << make_description(app);
0158 out << make_usage(app, name);
0159 out << make_positionals(app);
0160 out << make_groups(app, mode);
0161 out << make_subcommands(app, mode);
0162 out << make_footer(app);
0163
0164 return out.str();
0165 }
0166
0167 CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
0168 std::stringstream out;
0169
0170 std::vector<const App *> subcommands = app->get_subcommands({});
0171
0172
0173 std::vector<std::string> subcmd_groups_seen;
0174 for(const App *com : subcommands) {
0175 if(com->get_name().empty()) {
0176 if(!com->get_group().empty()) {
0177 out << make_expanded(com);
0178 }
0179 continue;
0180 }
0181 std::string group_key = com->get_group();
0182 if(!group_key.empty() &&
0183 std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
0184 return detail::to_lower(a) == detail::to_lower(group_key);
0185 }) == subcmd_groups_seen.end())
0186 subcmd_groups_seen.push_back(group_key);
0187 }
0188
0189
0190 for(const std::string &group : subcmd_groups_seen) {
0191 out << "\n" << group << ":\n";
0192 std::vector<const App *> subcommands_group = app->get_subcommands(
0193 [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); });
0194 for(const App *new_com : subcommands_group) {
0195 if(new_com->get_name().empty())
0196 continue;
0197 if(mode != AppFormatMode::All) {
0198 out << make_subcommand(new_com);
0199 } else {
0200 out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
0201 out << "\n";
0202 }
0203 }
0204 }
0205
0206 return out.str();
0207 }
0208
0209 CLI11_INLINE std::string Formatter::make_subcommand(const App *sub) const {
0210 std::stringstream out;
0211 detail::format_help(out, sub->get_display_name(true), sub->get_description(), column_width_);
0212 return out.str();
0213 }
0214
0215 CLI11_INLINE std::string Formatter::make_expanded(const App *sub) const {
0216 std::stringstream out;
0217 out << sub->get_display_name(true) << "\n";
0218
0219 out << make_description(sub);
0220 if(sub->get_name().empty() && !sub->get_aliases().empty()) {
0221 detail::format_aliases(out, sub->get_aliases(), column_width_ + 2);
0222 }
0223 out << make_positionals(sub);
0224 out << make_groups(sub, AppFormatMode::Sub);
0225 out << make_subcommands(sub, AppFormatMode::Sub);
0226
0227
0228 std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n");
0229 tmp = tmp.substr(0, tmp.size() - 1);
0230
0231
0232 return detail::find_and_replace(tmp, "\n", "\n ") + "\n";
0233 }
0234
0235 CLI11_INLINE std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
0236 if(is_positional)
0237 return opt->get_name(true, false);
0238
0239 return opt->get_name(false, true);
0240 }
0241
0242 CLI11_INLINE std::string Formatter::make_option_opts(const Option *opt) const {
0243 std::stringstream out;
0244
0245 if(!opt->get_option_text().empty()) {
0246 out << " " << opt->get_option_text();
0247 } else {
0248 if(opt->get_type_size() != 0) {
0249 if(!opt->get_type_name().empty())
0250 out << " " << get_label(opt->get_type_name());
0251 if(!opt->get_default_str().empty())
0252 out << " [" << opt->get_default_str() << "] ";
0253 if(opt->get_expected_max() == detail::expected_max_vector_size)
0254 out << " ...";
0255 else if(opt->get_expected_min() > 1)
0256 out << " x " << opt->get_expected();
0257
0258 if(opt->get_required())
0259 out << " " << get_label("REQUIRED");
0260 }
0261 if(!opt->get_envname().empty())
0262 out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
0263 if(!opt->get_needs().empty()) {
0264 out << " " << get_label("Needs") << ":";
0265 for(const Option *op : opt->get_needs())
0266 out << " " << op->get_name();
0267 }
0268 if(!opt->get_excludes().empty()) {
0269 out << " " << get_label("Excludes") << ":";
0270 for(const Option *op : opt->get_excludes())
0271 out << " " << op->get_name();
0272 }
0273 }
0274 return out.str();
0275 }
0276
0277 CLI11_INLINE std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
0278
0279 CLI11_INLINE std::string Formatter::make_option_usage(const Option *opt) const {
0280
0281 std::stringstream out;
0282 out << make_option_name(opt, true);
0283 if(opt->get_expected_max() >= detail::expected_max_vector_size)
0284 out << "...";
0285 else if(opt->get_expected_max() > 1)
0286 out << "(" << opt->get_expected() << "x)";
0287
0288 return opt->get_required() ? out.str() : "[" + out.str() + "]";
0289 }
0290
0291 }