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
21class PorytilesFormatter : public CLI::Formatter {
22 public:
23 PorytilesFormatter() : CLI::Formatter()
24 {
25 // Hide verbose type labels
26 label("REQUIRED", "");
27 label("TEXT", "");
28 label("INT", "");
29 label("UINT", "");
30 label("FLOAT", "");
31
32 // Adjust column width for better alignment
33 column_width(40);
34 }
35
40 std::string make_group(std::string group, bool is_positional, std::vector<const CLI::Option *> opts) const override
41 {
42 if (opts.empty()) {
43 return "";
44 }
45
46 std::stringstream out;
47
48 // Use clean header format like standard CLIs
49 out << "\n" << group << ":\n";
50
51 for (const auto *opt : opts) {
52 out << make_option(opt, is_positional);
53 }
54
55 return out.str();
56 }
57
65 std::string make_expanded(const CLI::App *sub, CLI::AppFormatMode mode) const override
66 {
67 std::stringstream out;
68
69 // For option groups (empty name, non-empty group), use the group name as header
70 // instead of get_display_name() which produces "[Option Group: ...]"
71 bool is_option_group = sub->get_name().empty() && !sub->get_group().empty();
72 std::string header;
73 if (is_option_group) {
74 header = sub->get_group();
75 }
76 else {
77 header = sub->get_display_name(true);
78 }
79
80 out << "\n" << header << ":\n";
81
82 // Include description if present
83 std::string desc = sub->get_description();
84 if (!desc.empty()) {
85 out << " " << desc << "\n";
86 }
87
88 // Include positionals
89 out << make_positionals(sub);
90
91 if (is_option_group) {
92 // For option groups, directly output options without group headers
93 // This avoids duplicate "OPTIONS:" headers and filters out help flags
94 std::vector<const CLI::Option *> opts = sub->get_options([sub](const CLI::Option *opt) {
95 return opt->nonpositional() && sub->get_help_ptr() != opt && sub->get_help_all_ptr() != opt;
96 });
97
98 for (const CLI::Option *opt : opts) {
99 out << make_option(opt, false);
100 }
101 }
102 else {
103 // For regular subcommands, use standard group formatting
104 out << make_groups(sub, mode);
105 }
106
107 // Include subcommands
108 out << make_subcommands(sub, mode);
109
110 return out.str();
111 }
112
120 std::string make_option_opts(const CLI::Option *opt) const override
121 {
122 std::stringstream out;
123
124 if (!opt->get_option_text().empty()) {
125 out << " " << opt->get_option_text();
126 }
127 else {
128 if (opt->get_type_size() != 0) {
129 if (!opt->get_type_name().empty()) {
130 std::string type_name = opt->get_type_name();
131
132 // Strip verbose transformer descriptions (everything after ":")
133 // This handles CheckedTransformer's "TYPE:value in {...}" format
134 std::size_t colon_pos = type_name.find(':');
135 if (colon_pos != std::string::npos) {
136 type_name = type_name.substr(0, colon_pos);
137 }
138
139 // Apply label transformation (may convert TEXT/INT/etc to empty)
140 std::string label_val = get_label(type_name);
141 if (!label_val.empty()) {
142 out << " " << label_val;
143 }
144 }
145 if (!opt->get_default_str().empty()) {
146 out << " [" << opt->get_default_str() << "]";
147 }
148 if (opt->get_expected_max() == CLI::detail::expected_max_vector_size) {
149 out << " ...";
150 }
151 else if (opt->get_expected_min() > 1) {
152 out << " x " << opt->get_expected();
153 }
154
155 if (opt->get_required()) {
156 std::string req_label = get_label("REQUIRED");
157 if (!req_label.empty()) {
158 out << " " << req_label;
159 }
160 }
161 }
162 if (!opt->get_envname().empty()) {
163 out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
164 }
165 if (!opt->get_needs().empty()) {
166 out << " " << get_label("Needs") << ":";
167 for (const CLI::Option *op : opt->get_needs()) {
168 out << " " << op->get_name();
169 }
170 }
171 if (!opt->get_excludes().empty()) {
172 out << " " << get_label("Excludes") << ":";
173 for (const CLI::Option *op : opt->get_excludes()) {
174 out << " " << op->get_name();
175 }
176 }
177 }
178 return out.str();
179 }
180};
181
182} // 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.