Sharg 1.1.2-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-2024, Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2024, 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 {
135 std::cout << '\n' << to_text("\\fB");
136 std::transform(title.begin(),
137 title.end(),
138 out,
139 [](unsigned char c)
140 {
141 return std::toupper(c);
142 });
143 std::cout << to_text("\\fP") << '\n';
144 prev_was_paragraph = false;
145 }
146
150 void print_subsection(std::string const & title)
151 {
153 std::cout << '\n';
154 std::fill_n(out, layout.leftPadding / 2, ' ');
155 std::cout << in_bold(title) << '\n';
156 prev_was_paragraph = false;
157 }
158
164 void print_line(std::string const & text, bool const line_is_paragraph)
165 {
166 if (prev_was_paragraph)
167 std::cout << '\n';
168
170 std::fill_n(out, layout.leftPadding, ' ');
171 print_text(text, layout.leftPadding);
172 prev_was_paragraph = line_is_paragraph;
173 }
174
189 void print_list_item(std::string const & term, std::string const & desc)
190 {
191 if (prev_was_paragraph)
192 std::cout << '\n';
193
195
196 // Print term.
197 std::fill_n(out, layout.leftPadding, ' ');
198 std::cout << to_text(term);
199 unsigned pos = layout.leftPadding + term.size();
200 if (pos + layout.centerPadding > layout.rightColumnTab)
201 {
202 std::cout << '\n';
203 pos = 0;
204 }
205 std::fill_n(out, layout.rightColumnTab - pos, ' ');
206 print_text(desc, layout.rightColumnTab);
207
208 prev_was_paragraph = false;
209 }
210
212 void print_footer()
213 {
214 // no footer
215 }
216
220 std::string to_text(std::string const & str)
221 {
222 std::string result;
223
224 for (auto it = str.begin(); it != str.end(); ++it)
225 {
226 if (*it == '\\')
227 {
228 // Handle escape sequence, we interpret only "\-", "\fI", and "\fB".
229 ++it;
230 assert(it != str.end());
231 if (*it == '-')
232 {
233 result.push_back(*it);
234 }
235 else if (*it == 'f')
236 {
237 ++it;
238 assert(it != str.end());
239 if (*it == 'I')
240 {
241 if (stdout_is_terminal())
242 result.append("\033[4m");
243 }
244 else if (*it == 'B')
245 {
246 if (stdout_is_terminal())
247 result.append("\033[1m");
248 }
249 else if (*it == 'P')
250 {
251 if (stdout_is_terminal())
252 result.append("\033[0m");
253 }
254 else
255 {
256 result.append("\\f");
257 result.push_back(*it);
258 }
259 }
260 else
261 {
262 result.push_back('\\');
263 result.push_back(*it);
264 }
265 }
266 else
267 {
268 result.push_back(*it);
269 }
270 }
271
272 return result;
273 }
274
279 unsigned text_width(std::string const & text)
280 {
281 unsigned result = 0;
282
283 for (unsigned i = 0; i < text.size(); ++i)
284 {
285 if (text[i] != '\\')
286 {
287 result += 1;
288 continue;
289 }
290
291 if (i + 1 == text.size())
292 {
293 result += 1; // Will print "\\".
294 continue;
295 }
296
297 if (text[i + 1] == '\\' || text[i + 1] == '-')
298 {
299 i += 1;
300 result += 1;
301 continue; // Will print '\\' or '-'.
302 }
303
304 if (i + 2 == text.size())
305 {
306 i += 1;
307 result += 2; // Will print two chars.
308 continue;
309 }
310
311 if (text[i + 1] == 'f')
312 {
313 if (text[i + 2] == 'B' || text[i + 2] == 'I' || text[i + 2] == 'P')
314 i += 2; // Skip f and {B, I, P}.
315 else
316 result += 1;
317 }
318 }
319
320 return result;
321 }
322
327 void print_text(std::string const & text, unsigned const tab)
328 {
329 unsigned pos = tab;
331
332 // Tokenize the text.
333 std::istringstream iss(text.c_str());
335
336 // Print the text.
337 assert(pos <= tab);
338 std::fill_n(out, tab - pos, ' '); // go to tab
339
340 pos = tab;
341 for (auto it = tokens.begin(); it != tokens.end(); ++it)
342 {
343 if (it == tokens.begin())
344 {
345 std::cout << to_text(*it);
346 pos += text_width(*it);
347 if (pos > layout.screenWidth)
348 {
349 std::cout << '\n';
350 std::fill_n(out, tab, ' ');
351 pos = tab;
352 }
353 }
354 else
355 {
356 if (pos + 1 + text_width(*it) > layout.screenWidth)
357 {
358 // Would go over screen with next, print current word on next line.
359 std::cout << '\n';
360 fill_n(out, tab, ' ');
361 std::cout << to_text(*it);
362 pos = tab + text_width(*it);
363 }
364 else
365 {
366 std::cout << ' ';
367 std::cout << to_text(*it);
368 pos += text_width(*it) + 1;
369 }
370 }
371 }
372 if (!empty(tokens))
373 std::cout << '\n';
374 }
375
380 std::string in_bold(std::string const & str)
381 {
382 return to_text("\\fB") + str + to_text("\\fP");
383 }
384
386 bool prev_was_paragraph{false};
387
389 friend struct ::sharg::detail::test_accessor;
390
392 console_layout_struct layout{};
393};
394
406class format_short_help : public format_help
407{
408public:
412 void parse(parser_meta_data const & parser_meta)
413 {
414 meta = parser_meta;
415
416 print_header();
417
418 if (!parser_meta.synopsis.empty())
419 print_synopsis();
420
421 print_line("Try -h or --help for more information.\n", true);
422 }
423};
424
436class format_version : public format_help
437{
438public:
442 void parse(parser_meta_data & parser_meta)
443 {
444 meta = parser_meta;
445
446 print_header();
447 print_version();
448 }
449};
450
462class format_copyright : public format_help
463{
464public:
468 void parse(parser_meta_data const & parser_meta)
469 {
470 meta = parser_meta;
471 std::string seqan_license{
472 R"(Copyright (c) 2006-2024, Knut Reinert & Freie Universität Berlin
473Copyright (c) 2016-2024, Knut Reinert & MPI für molekulare Genetik
474All rights reserved.
475
476Redistribution and use in source and binary forms, with or without
477modification, are permitted provided that the following conditions are met:
478
479 * Redistributions of source code must retain the above copyright
480 notice, this list of conditions and the following disclaimer.
481 * Redistributions in binary form must reproduce the above copyright
482 notice, this list of conditions and the following disclaimer in the
483 documentation and/or other materials provided with the distribution.
484 * Neither the name of Knut Reinert or the FU Berlin nor the names of
485 its contributors may be used to endorse or promote products derived
486 from this software without specific prior written permission.
487
488THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
489AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
490IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
491ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
492FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
493DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
494SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
495CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
496LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
497OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
498DAMAGE.)"};
499
500 std::cout << std::string(80, '=') << "\n"
501 << in_bold("Copyright information for " + meta.app_name + ":\n") << std::string(80, '-') << '\n';
502
503 if (!empty(meta.long_copyright))
504 {
505 std::cout << to_text("\\fP") << meta.long_copyright << "\n";
506 }
507 else if (!empty(meta.short_copyright))
508 {
509 std::cout << in_bold(meta.app_name + " full copyright information not available. "
510 + "Displaying short copyright information instead:\n")
511 << meta.short_copyright << "\n";
512 }
513 else
514 {
515 std::cout << to_text("\\fP") << meta.app_name << " copyright information not available.\n";
516 }
517
518 std::cout << std::string(80, '=') << '\n'
519 << in_bold("This program contains SeqAn code licensed under the following terms:\n")
520 << std::string(80, '-') << '\n'
521 << seqan_license << '\n';
522 }
523};
524
525} // 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:26
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.
T transform(T... args)
Hide me