From c24bf7541398ffeb68e7343154b93f9a630e2147 Mon Sep 17 00:00:00 2001 From: Daniel Herrera Castro Date: Tue, 9 Aug 2022 10:52:50 +0200 Subject: [PATCH] [precompile] Split Formatter.hpp --- CLI11.hpp.in | 2 ++ include/CLI/Formatter.hpp | 275 ++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- include/CLI/impl/Formatter_inl.hpp | 291 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/Formatter.cpp | 7 +++++++ 4 files changed, 304 insertions(+), 271 deletions(-) create mode 100644 include/CLI/impl/Formatter_inl.hpp create mode 100644 src/Formatter.cpp diff --git a/CLI11.hpp.in b/CLI11.hpp.in index 979f7cf..75e8ddb 100644 --- a/CLI11.hpp.in +++ b/CLI11.hpp.in @@ -72,4 +72,6 @@ namespace {namespace} {{ {formatter_hpp} +{formatter_inl_hpp} + }} // namespace {namespace} diff --git a/include/CLI/Formatter.hpp b/include/CLI/Formatter.hpp index cf4c0dd..c1eceac 100644 --- a/include/CLI/Formatter.hpp +++ b/include/CLI/Formatter.hpp @@ -17,276 +17,9 @@ namespace CLI { // [CLI11:formatter_hpp:verbatim] - -inline std::string -Formatter::make_group(std::string group, bool is_positional, std::vector opts) const { - std::stringstream out; - - out << "\n" << group << ":\n"; - for(const Option *opt : opts) { - out << make_option(opt, is_positional); - } - - return out.str(); -} - -inline std::string Formatter::make_positionals(const App *app) const { - std::vector opts = - app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); }); - - if(opts.empty()) - return {}; - - return make_group(get_label("Positionals"), true, opts); -} - -inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const { - std::stringstream out; - std::vector groups = app->get_groups(); - - // Options - for(const std::string &group : groups) { - std::vector opts = app->get_options([app, mode, &group](const Option *opt) { - return opt->get_group() == group // Must be in the right group - && opt->nonpositional() // Must not be a positional - && (mode != AppFormatMode::Sub // If mode is Sub, then - || (app->get_help_ptr() != opt // Ignore help pointer - && app->get_help_all_ptr() != opt)); // Ignore help all pointer - }); - if(!group.empty() && !opts.empty()) { - out << make_group(group, false, opts); - - if(group != groups.back()) - out << "\n"; - } - } - - return out.str(); -} - -inline std::string Formatter::make_description(const App *app) const { - std::string desc = app->get_description(); - auto min_options = app->get_require_option_min(); - auto max_options = app->get_require_option_max(); - if(app->get_required()) { - desc += " REQUIRED "; - } - if((max_options == min_options) && (min_options > 0)) { - if(min_options == 1) { - desc += " \n[Exactly 1 of the following options is required]"; - } else { - desc += " \n[Exactly " + std::to_string(min_options) + "options from the following list are required]"; - } - } else if(max_options > 0) { - if(min_options > 0) { - desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) + - " of the follow options are required]"; - } else { - desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]"; - } - } else if(min_options > 0) { - desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]"; - } - return (!desc.empty()) ? desc + "\n" : std::string{}; -} - -inline std::string Formatter::make_usage(const App *app, std::string name) const { - std::stringstream out; - - out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name; - - std::vector groups = app->get_groups(); - - // Print an Options badge if any options exist - std::vector 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 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 positional_names(positionals.size()); - std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) { - return make_option_usage(opt); - }); - - out << " " << detail::join(positional_names, " "); - } - - // Add a marker if subcommands are expected or optional - if(!app->get_subcommands( - [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); }) - .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 std::string{}; - } - return footer + "\n"; -} - -inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const { - - // This immediately forwards to the make_expanded method. This is done this way so that subcommands can - // have overridden formatters - if(mode == AppFormatMode::Sub) - return make_expanded(app); - - std::stringstream out; - if((app->get_name().empty()) && (app->get_parent() != nullptr)) { - if(app->get_group() != "Subcommands") { - out << app->get_group() << ':'; - } - } - - out << make_description(app); - out << make_usage(app, name); - out << make_positionals(app); - out << make_groups(app, mode); - out << make_subcommands(app, mode); - out << '\n' << make_footer(app); - - return out.str(); -} - -inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const { - std::stringstream out; - - std::vector subcommands = app->get_subcommands({}); - - // Make a list in definition order of the groups seen - std::vector subcmd_groups_seen; - for(const App *com : subcommands) { - if(com->get_name().empty()) { - if(!com->get_group().empty()) { - out << make_expanded(com); - } - continue; - } - 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"; - std::vector subcommands_group = app->get_subcommands( - [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); }); - for(const App *new_com : subcommands_group) { - if(new_com->get_name().empty()) - continue; - if(mode != AppFormatMode::All) { - out << make_subcommand(new_com); - } else { - out << new_com->help(new_com->get_name(), AppFormatMode::Sub); - out << "\n"; - } - } - } - - return out.str(); -} - -inline std::string Formatter::make_subcommand(const App *sub) const { - std::stringstream out; - detail::format_help(out, sub->get_display_name(true), sub->get_description(), column_width_); - return out.str(); -} - -inline std::string Formatter::make_expanded(const App *sub) const { - std::stringstream out; - out << sub->get_display_name(true) << "\n"; - - out << make_description(sub); - if(sub->get_name().empty() && !sub->get_aliases().empty()) { - detail::format_aliases(out, sub->get_aliases(), column_width_ + 2); - } - out << make_positionals(sub); - out << make_groups(sub, AppFormatMode::Sub); - out << make_subcommands(sub, AppFormatMode::Sub); - - // Drop blank spaces - std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n"); - tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n' - - // Indent all but the first line (the name) - return detail::find_and_replace(tmp, "\n", "\n ") + "\n"; -} - -inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const { - if(is_positional) - return opt->get_name(true, false); - - return opt->get_name(false, true); -} - -inline std::string Formatter::make_option_opts(const Option *opt) const { - std::stringstream out; - - if(!opt->get_option_text().empty()) { - out << " " << opt->get_option_text(); - } else { - if(opt->get_type_size() != 0) { - if(!opt->get_type_name().empty()) - out << " " << get_label(opt->get_type_name()); - if(!opt->get_default_str().empty()) - out << " [" << opt->get_default_str() << "] "; - if(opt->get_expected_max() == detail::expected_max_vector_size) - out << " ..."; - else if(opt->get_expected_min() > 1) - out << " x " << opt->get_expected(); - - 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_max() >= detail::expected_max_vector_size) - out << "..."; - else if(opt->get_expected_max() > 1) - out << "(" << opt->get_expected() << "x)"; - - return opt->get_required() ? out.str() : "[" + out.str() + "]"; -} - // [CLI11:formatter_hpp:end] } // namespace CLI + +#ifndef CLI11_COMPILE +#include "impl/Formatter_inl.hpp" +#endif diff --git a/include/CLI/impl/Formatter_inl.hpp b/include/CLI/impl/Formatter_inl.hpp new file mode 100644 index 0000000..37249e0 --- /dev/null +++ b/include/CLI/impl/Formatter_inl.hpp @@ -0,0 +1,291 @@ +// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +// This include is only needed for IDEs to discover symbols +#include + +// [CLI11:public_includes:set] +#include +#include +#include +#include +// [CLI11:public_includes:end] + +namespace CLI { +// [CLI11:formatter_inl_hpp:verbatim] +CLI11_INLINE std::string +Formatter::make_group(std::string group, bool is_positional, std::vector opts) const { + std::stringstream out; + + out << "\n" << group << ":\n"; + for(const Option *opt : opts) { + out << make_option(opt, is_positional); + } + + return out.str(); +} + +CLI11_INLINE std::string Formatter::make_positionals(const App *app) const { + std::vector opts = + app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); }); + + if(opts.empty()) + return {}; + + return make_group(get_label("Positionals"), true, opts); +} + +CLI11_INLINE std::string Formatter::make_groups(const App *app, AppFormatMode mode) const { + std::stringstream out; + std::vector groups = app->get_groups(); + + // Options + for(const std::string &group : groups) { + std::vector opts = app->get_options([app, mode, &group](const Option *opt) { + return opt->get_group() == group // Must be in the right group + && opt->nonpositional() // Must not be a positional + && (mode != AppFormatMode::Sub // If mode is Sub, then + || (app->get_help_ptr() != opt // Ignore help pointer + && app->get_help_all_ptr() != opt)); // Ignore help all pointer + }); + if(!group.empty() && !opts.empty()) { + out << make_group(group, false, opts); + + if(group != groups.back()) + out << "\n"; + } + } + + return out.str(); +} + +CLI11_INLINE std::string Formatter::make_description(const App *app) const { + std::string desc = app->get_description(); + auto min_options = app->get_require_option_min(); + auto max_options = app->get_require_option_max(); + if(app->get_required()) { + desc += " REQUIRED "; + } + if((max_options == min_options) && (min_options > 0)) { + if(min_options == 1) { + desc += " \n[Exactly 1 of the following options is required]"; + } else { + desc += " \n[Exactly " + std::to_string(min_options) + "options from the following list are required]"; + } + } else if(max_options > 0) { + if(min_options > 0) { + desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) + + " of the follow options are required]"; + } else { + desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]"; + } + } else if(min_options > 0) { + desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]"; + } + return (!desc.empty()) ? desc + "\n" : std::string{}; +} + +CLI11_INLINE std::string Formatter::make_usage(const App *app, std::string name) const { + std::stringstream out; + + out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name; + + std::vector groups = app->get_groups(); + + // Print an Options badge if any options exist + std::vector 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 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 positional_names(positionals.size()); + std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) { + return make_option_usage(opt); + }); + + out << " " << detail::join(positional_names, " "); + } + + // Add a marker if subcommands are expected or optional + if(!app->get_subcommands( + [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); }) + .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(); +} + +CLI11_INLINE std::string Formatter::make_footer(const App *app) const { + std::string footer = app->get_footer(); + if(footer.empty()) { + return std::string{}; + } + return footer + "\n"; +} + +CLI11_INLINE std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const { + + // This immediately forwards to the make_expanded method. This is done this way so that subcommands can + // have overridden formatters + if(mode == AppFormatMode::Sub) + return make_expanded(app); + + std::stringstream out; + if((app->get_name().empty()) && (app->get_parent() != nullptr)) { + if(app->get_group() != "Subcommands") { + out << app->get_group() << ':'; + } + } + + out << make_description(app); + out << make_usage(app, name); + out << make_positionals(app); + out << make_groups(app, mode); + out << make_subcommands(app, mode); + out << '\n' << make_footer(app); + + return out.str(); +} + +CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const { + std::stringstream out; + + std::vector subcommands = app->get_subcommands({}); + + // Make a list in definition order of the groups seen + std::vector subcmd_groups_seen; + for(const App *com : subcommands) { + if(com->get_name().empty()) { + if(!com->get_group().empty()) { + out << make_expanded(com); + } + continue; + } + 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"; + std::vector subcommands_group = app->get_subcommands( + [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); }); + for(const App *new_com : subcommands_group) { + if(new_com->get_name().empty()) + continue; + if(mode != AppFormatMode::All) { + out << make_subcommand(new_com); + } else { + out << new_com->help(new_com->get_name(), AppFormatMode::Sub); + out << "\n"; + } + } + } + + return out.str(); +} + +CLI11_INLINE std::string Formatter::make_subcommand(const App *sub) const { + std::stringstream out; + detail::format_help(out, sub->get_display_name(true), sub->get_description(), column_width_); + return out.str(); +} + +CLI11_INLINE std::string Formatter::make_expanded(const App *sub) const { + std::stringstream out; + out << sub->get_display_name(true) << "\n"; + + out << make_description(sub); + if(sub->get_name().empty() && !sub->get_aliases().empty()) { + detail::format_aliases(out, sub->get_aliases(), column_width_ + 2); + } + out << make_positionals(sub); + out << make_groups(sub, AppFormatMode::Sub); + out << make_subcommands(sub, AppFormatMode::Sub); + + // Drop blank spaces + std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n"); + tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n' + + // Indent all but the first line (the name) + return detail::find_and_replace(tmp, "\n", "\n ") + "\n"; +} + +CLI11_INLINE std::string Formatter::make_option_name(const Option *opt, bool is_positional) const { + if(is_positional) + return opt->get_name(true, false); + + return opt->get_name(false, true); +} + +CLI11_INLINE std::string Formatter::make_option_opts(const Option *opt) const { + std::stringstream out; + + if(!opt->get_option_text().empty()) { + out << " " << opt->get_option_text(); + } else { + if(opt->get_type_size() != 0) { + if(!opt->get_type_name().empty()) + out << " " << get_label(opt->get_type_name()); + if(!opt->get_default_str().empty()) + out << " [" << opt->get_default_str() << "] "; + if(opt->get_expected_max() == detail::expected_max_vector_size) + out << " ..."; + else if(opt->get_expected_min() > 1) + out << " x " << opt->get_expected(); + + 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(); +} + +CLI11_INLINE std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); } + +CLI11_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_max() >= detail::expected_max_vector_size) + out << "..."; + else if(opt->get_expected_max() > 1) + out << "(" << opt->get_expected() << "x)"; + + return opt->get_required() ? out.str() : "[" + out.str() + "]"; +} +// [CLI11:formatter_inl_hpp:end] +} // namespace CLI diff --git a/src/Formatter.cpp b/src/Formatter.cpp new file mode 100644 index 0000000..f0ce80e --- /dev/null +++ b/src/Formatter.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#include -- libgit2 0.21.4