Porytiles
Loading...
Searching...
No Matches
custom_formatter.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <iomanip>
5#include <sstream>
6#include <string>
7#include <vector>
8
9#include "CLI/CLI.hpp"
10
11namespace porytiles {
12
23class PorytilesFormatter : public CLI::Formatter {
24 public:
25 PorytilesFormatter() : CLI::Formatter()
26 {
27 // Hide verbose type labels
28 label("REQUIRED", "");
29 label("TEXT", "");
30 label("INT", "");
31 label("UINT", "");
32 label("FLOAT", "");
33
34 // Adjust column width for better alignment
35 column_width(40);
36 }
37
44 std::string make_group(std::string group, bool is_positional, std::vector<const CLI::Option *> opts) const override
45 {
46 if (opts.empty()) {
47 return "";
48 }
49
50 std::stringstream out;
51
52 // Use clean header format like standard CLIs
53 out << "\n" << group << ":\n";
54
55 for (const auto *opt : opts) {
56 out << make_option(opt, is_positional);
57 }
58
59 return out.str();
60 }
61
71 std::string make_expanded(const CLI::App *sub, CLI::AppFormatMode mode) const override
72 {
73 std::stringstream out;
74
75 // For option groups (empty name, non-empty group), use the group name as header
76 // instead of get_display_name() which produces "[Option Group: ...]"
77 bool is_option_group = sub->get_name().empty() && !sub->get_group().empty();
78 std::string header;
79 if (is_option_group) {
80 header = sub->get_group();
81 }
82 else {
83 header = sub->get_display_name(true);
84 }
85
86 out << "\n" << header << ":\n";
87
88 // Include description if present
89 std::string desc = sub->get_description();
90 if (!desc.empty()) {
91 out << " " << desc << "\n";
92 }
93
94 // Include positionals
95 out << make_positionals(sub);
96
97 if (is_option_group) {
98 // For option groups, directly output options without group headers
99 // This avoids duplicate "OPTIONS:" headers and filters out help flags
100 std::vector<const CLI::Option *> opts = sub->get_options([sub](const CLI::Option *opt) {
101 return opt->nonpositional() && sub->get_help_ptr() != opt && sub->get_help_all_ptr() != opt;
102 });
103
104 for (const CLI::Option *opt : opts) {
105 out << make_option(opt, false);
106 }
107 }
108 else {
109 // For regular subcommands, use standard group formatting
110 out << make_groups(sub, mode);
111 }
112
113 // Include subcommands
114 out << make_subcommands(sub, mode);
115
116 return out.str();
117 }
118
128 std::string make_option_opts(const CLI::Option *opt) const override
129 {
130 std::stringstream out;
131
132 if (!opt->get_option_text().empty()) {
133 out << " " << opt->get_option_text();
134 }
135 else {
136 if (opt->get_type_size() != 0) {
137 if (!opt->get_type_name().empty()) {
138 std::string type_name = opt->get_type_name();
139
140 // Strip verbose transformer descriptions (everything after ":")
141 // This handles CheckedTransformer's "TYPE:value in {...}" format
142 std::size_t colon_pos = type_name.find(':');
143 if (colon_pos != std::string::npos) {
144 type_name = type_name.substr(0, colon_pos);
145 }
146
147 // Apply label transformation (may convert TEXT/INT/etc to empty)
148 std::string label_val = get_label(type_name);
149 if (!label_val.empty()) {
150 out << " " << label_val;
151 }
152 }
153 if (!opt->get_default_str().empty()) {
154 out << " [" << opt->get_default_str() << "]";
155 }
156 if (opt->get_expected_max() == CLI::detail::expected_max_vector_size) {
157 out << " ...";
158 }
159 else if (opt->get_expected_min() > 1) {
160 out << " x " << opt->get_expected();
161 }
162
163 if (opt->get_required()) {
164 std::string req_label = get_label("REQUIRED");
165 if (!req_label.empty()) {
166 out << " " << req_label;
167 }
168 }
169 }
170 if (!opt->get_envname().empty()) {
171 out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
172 }
173 if (!opt->get_needs().empty()) {
174 out << " " << get_label("Needs") << ":";
175 for (const CLI::Option *op : opt->get_needs()) {
176 out << " " << op->get_name();
177 }
178 }
179 if (!opt->get_excludes().empty()) {
180 out << " " << get_label("Excludes") << ":";
181 for (const CLI::Option *op : opt->get_excludes()) {
182 out << " " << op->get_name();
183 }
184 }
185 }
186 return out.str();
187 }
188};
189
190} // namespace porytiles
Custom CLI11 formatter for cleaner help output.
std::string make_group(std::string group, bool is_positional, std::vector< const CLI::Option * > opts) const override
Override to customize option group headers.
std::string make_option_opts(const CLI::Option *opt) const override
Override to strip verbose transformer descriptions from type names.
std::string make_expanded(const CLI::App *sub, CLI::AppFormatMode mode) const override
Override to handle option groups (inline subcommands) with clean headers.