Sharg 1.2.3-rc.1
The argument parser for bio-c++ tools.
Loading...
Searching...
No Matches
format_help.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2006-2026, Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2026, Knut Reinert & MPI für molekulare Genetik
3// SPDX-License-Identifier: BSD-3-Clause
4
12#pragma once
13
17
18namespace sharg::detail
19{
20
33class format_help : public format_help_base<format_help>
34{
36 using base_type = format_help_base<format_help>;
37
39 friend base_type;
40
41public:
45 format_help() = default;
46 format_help(format_help const & pf) = default;
47 format_help & operator=(format_help const &) = default;
48 format_help(format_help &&) = default;
49 format_help & operator=(format_help &&) = default;
50 ~format_help() = default;
51
53 format_help(std::vector<std::string> const & names,
54 update_notifications const version_updates,
55 bool const advanced = false) :
56 base_type{names, version_updates, advanced} {};
58
59protected:
62 struct console_layout_struct
63 {
65 uint32_t screenWidth;
67 uint32_t defaultScreenWidth;
69 uint32_t maximalScreenWidth;
71 uint32_t minimalScreenWidth;
73 uint32_t leftPadding;
75 uint32_t centerPadding;
77 uint32_t rightPadding;
79 uint32_t leftColumnWidth;
81 uint32_t rightColumnWidth;
83 uint32_t rightColumnTab;
84
87 console_layout_struct(uint32_t const terminal_width) :
88 screenWidth{0},
89 defaultScreenWidth{80},
90 maximalScreenWidth{120},
91 minimalScreenWidth{40},
92 leftPadding{4},
93 centerPadding{2},
94 rightPadding{2},
95 leftColumnWidth{4},
96 rightColumnWidth{0}
97 {
98 // Guess terminal screen width and set into layout.
99 screenWidth = (terminal_width > 0) ? terminal_width : defaultScreenWidth;
100 screenWidth = std::max(screenWidth, minimalScreenWidth);
101 screenWidth = std::min(screenWidth, maximalScreenWidth);
102 screenWidth -= rightPadding;
103
104 rightColumnWidth = screenWidth - leftPadding - leftColumnWidth - centerPadding - rightPadding;
105 rightColumnTab = leftPadding + leftColumnWidth + centerPadding;
106 }
107
109 console_layout_struct() : console_layout_struct{get_terminal_width()}
110 {}
111 };
112
114 void print_header()
115 {
117
118 std::cout << meta.app_name;
119 if (!empty(meta.short_description))
120 std::cout << " - " << meta.short_description;
121
122 std::cout << "\n";
123 unsigned len =
124 text_width(meta.app_name) + (empty(meta.short_description) ? 0 : 3) + text_width(meta.short_description);
125 std::fill_n(out, len, '=');
126 std::cout << '\n';
127 }
128
132 void print_section(std::string const & title)
133 {
134 std::cout << '\n' << to_text("\\fB");
135 print_as_uppercase(title);
136 std::cout << to_text("\\fP") << '\n';
137 prev_was_paragraph = false;
138 }
139
143 void print_subsection(std::string const & title)
144 {
146 std::cout << '\n';
147 std::fill_n(out, layout.leftPadding / 2, ' ');
148 std::cout << in_bold(title) << '\n';
149 prev_was_paragraph = false;
150 }
151
157 void print_line(std::string const & text, bool const line_is_paragraph)
158 {
159 if (prev_was_paragraph)
160 std::cout << '\n';
161
163 std::fill_n(out, layout.leftPadding, ' ');
164 print_text(text, layout.leftPadding);
165 prev_was_paragraph = line_is_paragraph;
166 }
167
182 void print_list_item(std::string const & term, std::string const & desc)
183 {
184 if (prev_was_paragraph)
185 std::cout << '\n';
186
188
189 // Print term.
190 std::fill_n(out, layout.leftPadding, ' ');
191 std::cout << to_text(term);
192 unsigned pos = layout.leftPadding + text_width(term);
193 if (pos + layout.centerPadding > layout.rightColumnTab)
194 {
195 std::cout << '\n';
196 pos = 0;
197 }
198 if (!desc.empty())
199 {
200 std::fill_n(out, layout.rightColumnTab - pos, ' ');
201 print_text(desc, layout.rightColumnTab);
202 }
203
204 prev_was_paragraph = false;
205 }
206
208 void print_footer()
209 {
210 // no footer
211 }
212
216 std::string to_text(std::string const & str)
217 {
218 std::string result;
219
220 for (auto it = str.begin(); it != str.end(); ++it)
221 {
222 if (*it == '\\')
223 {
224 // Handle escape sequence, we interpret only "\-", "\fI", and "\fB".
225 ++it;
226 assert(it != str.end());
227 if (*it == '-')
228 {
229 result.push_back(*it);
230 }
231 else if (*it == 'f')
232 {
233 ++it;
234 assert(it != str.end());
235 if (*it == 'I')
236 {
237 if (stdout_is_terminal())
238 result.append("\033[4m");
239 }
240 else if (*it == 'B')
241 {
242 if (stdout_is_terminal())
243 result.append("\033[1m");
244 }
245 else if (*it == 'P')
246 {
247 if (stdout_is_terminal())
248 result.append("\033[0m");
249 }
250 else
251 {
252 result.append("\\f");
253 result.push_back(*it);
254 }
255 }
256 else
257 {
258 result.push_back('\\');
259 result.push_back(*it);
260 }
261 }
262 else
263 {
264 result.push_back(*it);
265 }
266 }
267
268 return result;
269 }
270
275 unsigned text_width(std::string const & text)
276 {
277 unsigned result = 0;
278
279 for (unsigned i = 0; i < text.size(); ++i)
280 {
281 if (text[i] != '\\')
282 {
283 result += 1;
284 continue;
285 }
286
287 if (i + 1 == text.size())
288 {
289 result += 1; // Will print "\\".
290 continue;
291 }
292
293 if (text[i + 1] == '\\' || text[i + 1] == '-')
294 {
295 i += 1;
296 result += 1;
297 continue; // Will print '\\' or '-'.
298 }
299
300 if (i + 2 == text.size())
301 {
302 i += 1;
303 result += 2; // Will print two chars.
304 continue;
305 }
306
307 if (text[i + 1] == 'f')
308 {
309 if (text[i + 2] == 'B' || text[i + 2] == 'I' || text[i + 2] == 'P')
310 i += 2; // Skip f and {B, I, P}.
311 else
312 result += 1;
313 }
314 }
315
316 return result;
317 }
318
323 void print_text(std::string const & text, unsigned const tab)
324 {
325 unsigned pos = tab;
327
328 // Tokenize the text.
329 std::istringstream iss(text.c_str());
331
332 // Print the text.
333 assert(pos <= tab);
334 std::fill_n(out, tab - pos, ' '); // go to tab
335
336 pos = tab;
337 for (auto it = tokens.begin(); it != tokens.end(); ++it)
338 {
339 if (it == tokens.begin())
340 {
341 std::cout << to_text(*it);
342 pos += text_width(*it);
343 if (pos > layout.screenWidth)
344 {
345 std::cout << '\n';
346 std::fill_n(out, tab, ' ');
347 pos = tab;
348 }
349 }
350 else
351 {
352 if (pos + 1 + text_width(*it) > layout.screenWidth)
353 {
354 // Would go over screen with next, print current word on next line.
355 std::cout << '\n';
356 fill_n(out, tab, ' ');
357 std::cout << to_text(*it);
358 pos = tab + text_width(*it);
359 }
360 else
361 {
362 std::cout << ' ';
363 std::cout << to_text(*it);
364 pos += text_width(*it) + 1;
365 }
366 }
367 }
368 if (!empty(tokens))
369 std::cout << '\n';
370 }
371
376 std::string in_bold(std::string const & str)
377 {
378 return to_text("\\fB") + str + to_text("\\fP");
379 }
380
382 bool prev_was_paragraph{false};
383
385 friend struct ::sharg::detail::test_accessor;
386
388 console_layout_struct layout{};
389};
390
402class format_short_help : public format_help
403{
404public:
410 void parse(parser_meta_data const & parser_meta, std::vector<std::string> const & executable_name)
411 {
412 meta = parser_meta;
413
414 print_header();
415
416 if (meta.synopsis.empty())
417 generate_default_synopsis(executable_name);
418
419 // Synopsis can be disabled by setting `parser.info.synopsis = {""};`
420 if (!meta.synopsis.empty() && !meta.synopsis.front().empty())
421 print_synopsis();
422
423 print_line("Try -h or --help for more information.\n", true);
424 }
425};
426
438class format_version : public format_help
439{
440public:
444 void parse(parser_meta_data & parser_meta, std::vector<std::string> const & /*executable_name*/)
445 {
446 meta = parser_meta;
447
448 print_header();
449 print_version();
450 }
451};
452
464class format_copyright : public format_help
465{
466public:
470 void parse(parser_meta_data const & parser_meta, std::vector<std::string> const & /*executable_name*/)
471 {
472 meta = parser_meta;
473 std::string seqan_license{
474 R"(Copyright (c) 2006-2026, Knut Reinert & Freie Universität Berlin
475Copyright (c) 2016-2026, Knut Reinert & MPI für molekulare Genetik
476All rights reserved.
477
478Redistribution and use in source and binary forms, with or without
479modification, are permitted provided that the following conditions are met:
480
481 * Redistributions of source code must retain the above copyright
482 notice, this list of conditions and the following disclaimer.
483 * Redistributions in binary form must reproduce the above copyright
484 notice, this list of conditions and the following disclaimer in the
485 documentation and/or other materials provided with the distribution.
486 * Neither the name of Knut Reinert or the FU Berlin nor the names of
487 its contributors may be used to endorse or promote products derived
488 from this software without specific prior written permission.
489
490THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
491AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
492IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
493ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
494FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
495DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
496SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
497CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
498LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
499OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
500DAMAGE.)"};
501
502 std::cout << std::string(80, '=') << "\n"
503 << in_bold("Copyright information for " + meta.app_name + ":\n") << std::string(80, '-') << '\n';
504
505 if (!empty(meta.long_copyright))
506 {
507 std::cout << to_text("\\fP") << meta.long_copyright << "\n";
508 }
509 else if (!empty(meta.short_copyright))
510 {
511 std::cout << in_bold(meta.app_name + " full copyright information not available. "
512 + "Displaying short copyright information instead:\n")
513 << meta.short_copyright << "\n";
514 }
515 else
516 {
517 std::cout << to_text("\\fP") << meta.app_name << " copyright information not available.\n";
518 }
519
520 std::cout << std::string(80, '=') << '\n'
521 << in_bold("This program contains SeqAn code licensed under the following terms:\n")
522 << std::string(80, '-') << '\n'
523 << seqan_license << '\n';
524 }
525};
526
527} // namespace sharg::detail
T append(T... args)
T begin(T... args)
T c_str(T... args)
T empty(T... args)
T end(T... args)
T fill_n(T... args)
Provides the format_base struct containing all helper functions that are needed in all formats.
update_notifications
Indicates whether application allows automatic update notifications by the sharg::parser.
Definition auxiliary.hpp:27
T is_same_v
T max(T... args)
T min(T... args)
T parse(T... args)
T push_back(T... args)
T size(T... args)
Checks if program is run interactively and retrieves dimensions of terminal (Transferred from seqan2)...
Forward declares sharg::detail::test_accessor.
Hide me