Loading...
Searching...
No Matches
format_help.hpp
Go to the documentation of this file.
// SPDX-FileCopyrightText: 2006-2025 Knut Reinert & Freie Universität Berlin
// SPDX-FileCopyrightText: 2016-2025 Knut Reinert & MPI für molekulare Genetik
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include <cassert>
#include <iostream>
#include <seqan3/argument_parser/detail/format_base.hpp>
#include <seqan3/argument_parser/detail/terminal.hpp>
#include <seqan3/core/detail/test_accessor.hpp>
namespace seqan3::detail
{
class format_help : public format_help_base<format_help>
{
using base_type = format_help_base<format_help>;
friend base_type;
public:
format_help() = default;
format_help(format_help const & pf) = default;
format_help & operator=(format_help const &) = default;
format_help(format_help &&) = default;
format_help & operator=(format_help &&) = default;
~format_help() = default;
format_help(std::vector<std::string> const & names, bool const advanced = false) : base_type{names, advanced} {};
protected:
struct console_layout_struct
{
uint32_t screenWidth;
uint32_t defaultScreenWidth;
uint32_t maximalScreenWidth;
uint32_t minimalScreenWidth;
uint32_t leftPadding;
uint32_t centerPadding;
uint32_t rightPadding;
uint32_t leftColumnWidth;
uint32_t rightColumnWidth;
uint32_t rightColumnTab;
console_layout_struct(uint32_t const terminal_width) :
screenWidth{0},
defaultScreenWidth{80},
maximalScreenWidth{120},
minimalScreenWidth{40},
leftPadding{4},
centerPadding{2},
rightPadding{2},
leftColumnWidth{4},
rightColumnWidth{0}
{
// Guess terminal screen width and set into layout.
screenWidth = (terminal_width > 0) ? terminal_width : defaultScreenWidth;
screenWidth = std::max(screenWidth, minimalScreenWidth);
screenWidth = std::min(screenWidth, maximalScreenWidth);
screenWidth -= rightPadding;
rightColumnWidth = screenWidth - leftPadding - leftColumnWidth - centerPadding - rightPadding;
rightColumnTab = leftPadding + leftColumnWidth + centerPadding;
}
console_layout_struct() : console_layout_struct{get_terminal_width()}
{}
};
void print_header()
{
std::ostream_iterator<char> out(std::cout);
std::cout << meta.app_name;
if (!empty(meta.short_description))
std::cout << " - " << meta.short_description;
std::cout << "\n";
unsigned len =
text_width(meta.app_name) + (empty(meta.short_description) ? 0 : 3) + text_width(meta.short_description);
std::fill_n(out, len, '=');
std::cout << '\n';
}
void print_section(std::string const & title)
{
std::ostream_iterator<char> out(std::cout);
std::cout << '\n' << to_text("\\fB");
std::transform(title.begin(),
title.end(),
out,
[](unsigned char c)
{
return std::toupper(c);
});
std::cout << to_text("\\fP") << '\n';
prev_was_paragraph = false;
}
void print_subsection(std::string const & title)
{
std::ostream_iterator<char> out(std::cout);
std::cout << '\n';
std::fill_n(out, layout.leftPadding / 2, ' ');
std::cout << in_bold(title) << '\n';
prev_was_paragraph = false;
}
void print_line(std::string const & text, bool const line_is_paragraph)
{
if (prev_was_paragraph)
std::cout << '\n';
std::ostream_iterator<char> out(std::cout);
std::fill_n(out, layout.leftPadding, ' ');
print_text(text, layout.leftPadding);
prev_was_paragraph = line_is_paragraph;
}
void print_list_item(std::string const & term, std::string const & desc)
{
if (prev_was_paragraph)
std::cout << '\n';
std::ostream_iterator<char> out(std::cout);
// Print term.
std::fill_n(out, layout.leftPadding, ' ');
std::cout << to_text(term);
unsigned pos = layout.leftPadding + term.size();
if (pos + layout.centerPadding > layout.rightColumnTab)
{
std::cout << '\n';
pos = 0;
}
std::fill_n(out, layout.rightColumnTab - pos, ' ');
print_text(desc, layout.rightColumnTab);
prev_was_paragraph = false;
}
void print_footer()
{
// no footer
}
std::string to_text(std::string const & str)
{
std::string result;
for (auto it = str.begin(); it != str.end(); ++it)
{
if (*it == '\\')
{
// Handle escape sequence, we interpret only "\-", "\fI", and "\fB".
++it;
assert(it != str.end());
if (*it == '-')
{
result.push_back(*it);
}
else if (*it == 'f')
{
++it;
assert(it != str.end());
if (*it == 'I')
{
if (is_terminal())
result.append("\033[4m");
}
else if (*it == 'B')
{
if (is_terminal())
result.append("\033[1m");
}
else if (*it == 'P')
{
if (is_terminal())
result.append("\033[0m");
}
else
{
result.append("\\f");
result.push_back(*it);
}
}
else
{
result.push_back('\\');
result.push_back(*it);
}
}
else
{
result.push_back(*it);
}
}
return result;
}
unsigned text_width(std::string const & text)
{
unsigned result = 0;
for (unsigned i = 0; i < text.size(); ++i)
{
if (text[i] != '\\')
{
result += 1;
continue;
}
if (i + 1 == text.size())
{
result += 1; // Will print "\\".
continue;
}
if (text[i + 1] == '\\' || text[i + 1] == '-')
{
i += 1;
result += 1;
continue; // Will print '\\' or '-'.
}
if (i + 2 == text.size())
{
i += 1;
result += 2; // Will print two chars.
continue;
}
if (text[i + 1] == 'f')
{
if (text[i + 2] == 'B' || text[i + 2] == 'I' || text[i + 2] == 'P')
i += 2; // Skip f and {B, I, P}.
else
result += 1;
}
}
return result;
}
void print_text(std::string const & text, unsigned const tab)
{
unsigned pos = tab;
std::ostream_iterator<char> out(std::cout);
// Tokenize the text.
std::istringstream iss(text.c_str());
std::vector<std::string> tokens;
std::ranges::copy(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter(tokens));
// Print the text.
assert(pos <= tab);
std::fill_n(out, tab - pos, ' '); // go to tab
pos = tab;
typedef std::vector<std::string>::const_iterator TConstIter;
for (TConstIter it = tokens.begin(); it != tokens.end(); ++it)
{
if (it == tokens.begin())
{
std::cout << to_text(*it);
pos += text_width(*it);
if (pos > layout.screenWidth)
{
std::cout << '\n';
std::fill_n(out, tab, ' ');
pos = tab;
}
}
else
{
if (pos + 1 + text_width(*it) > layout.screenWidth)
{
// Would go over screen with next, print current word on next line.
std::cout << '\n';
fill_n(out, tab, ' ');
std::cout << to_text(*it);
pos = tab + text_width(*it);
}
else
{
std::cout << ' ';
std::cout << to_text(*it);
pos += text_width(*it) + 1;
}
}
}
if (!empty(tokens))
std::cout << '\n';
}
std::string in_bold(std::string const & str)
{
return to_text("\\fB") + str + to_text("\\fP");
}
bool prev_was_paragraph{false};
friend struct ::seqan3::detail::test_accessor;
console_layout_struct layout{};
};
class format_short_help : public format_help
{
public:
void parse(argument_parser_meta_data const & parser_meta)
{
meta = parser_meta;
print_header();
if (!parser_meta.synopsis.empty())
print_synopsis();
print_line("Try -h or --help for more information.\n", true);
std::exit(EXIT_SUCCESS);
}
};
class format_version : public format_help
{
public:
void parse(argument_parser_meta_data & parser_meta)
{
meta = parser_meta;
print_header();
print_version();
std::exit(EXIT_SUCCESS); // program should not continue from here
}
};
class format_copyright : public format_help
{
public:
void parse(argument_parser_meta_data const & parser_meta)
{
meta = parser_meta;
debug_stream_type stream{std::cout};
std::string seqan_license{
R"(Copyright (c) 2006-2025, Knut Reinert & Freie Universität Berlin
Copyright (c) 2016-2025, Knut Reinert & MPI für molekulare Genetik
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Knut Reinert or the FU Berlin nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.)"};
stream << std::string(80, '=') << "\n"
<< in_bold("Copyright information for " + meta.app_name + ":\n") << std::string(80, '-') << '\n';
if (!empty(meta.long_copyright))
{
stream << to_text("\\fP") << meta.long_copyright << "\n";
}
else if (!empty(meta.short_copyright))
{
stream << in_bold(meta.app_name + " full copyright information not available. "
+ "Displaying short copyright information instead:\n")
<< meta.short_copyright << "\n";
}
else
{
stream << to_text("\\fP") << meta.app_name << " copyright information not available.\n";
}
stream << std::string(80, '=') << '\n'
<< in_bold("This program contains SeqAn code licensed under the following terms:\n")
<< std::string(80, '-') << '\n'
<< seqan_license << '\n';
std::exit(EXIT_SUCCESS);
}
};
} // namespace seqan3::detail
std::string::append
T append(T... args)
std::back_inserter
T back_inserter(T... args)
std::string::begin
T begin(T... args)
std::string::c_str
T c_str(T... args)
cassert
std::empty
T empty(T... args)
std::string::end
T end(T... args)
std::exit
T exit(T... args)
std::fill_n
T fill_n(T... args)
format_base.hpp
Provides the format_base struct containing all helper functions that are needed in all formats.
seqan3::advanced
@ advanced
Definition auxiliary.hpp:256
iostream
std::max
T max(T... args)
std::min
T min(T... args)
std::string::push_back
T push_back(T... args)
std::string::size
T size(T... args)
terminal.hpp
Checks if program is run interactively and retrieves dimensions of terminal (Transferred from seqan2)...
test_accessor.hpp
Forward declares seqan3::detail::test_accessor.
std::transform
T transform(T... args)
Hide me