Make use of escape codes configurable
This commit is contained in:
parent
6933b7b33e
commit
9829dbe727
|
@ -147,6 +147,18 @@ if(FORCE_UTF8_CODEPAGE)
|
||||||
list(APPEND META_PRIVATE_COMPILE_DEFINITIONS ${META_PROJECT_VARNAME}_FORCE_UTF8_CODEPAGE)
|
list(APPEND META_PRIVATE_COMPILE_DEFINITIONS ${META_PROJECT_VARNAME}_FORCE_UTF8_CODEPAGE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# configure whether escape codes should be enabled by default
|
||||||
|
option(ENABLE_ESCAPE_CODES_BY_DEAULT "enables usage of escape codes by default" ON)
|
||||||
|
if(ENABLE_ESCAPE_CODES_BY_DEAULT)
|
||||||
|
set_source_files_properties(
|
||||||
|
application/argumentparser.cpp
|
||||||
|
io/ansiescapecodes.cpp
|
||||||
|
PROPERTIES COMPILE_DEFINITIONS ${META_PROJECT_VARNAME}_ESCAPE_CODES_ENABLED_BY_DEFAULT
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Disabling use of escape codes by default.")
|
||||||
|
endif()
|
||||||
|
|
||||||
# include modules to apply configuration
|
# include modules to apply configuration
|
||||||
include(BasicConfig)
|
include(BasicConfig)
|
||||||
include(WindowsResources)
|
include(WindowsResources)
|
||||||
|
|
|
@ -782,7 +782,9 @@ void ArgumentParser::readArgs(int argc, const char *const *argv)
|
||||||
completionMode);
|
completionMode);
|
||||||
try {
|
try {
|
||||||
reader.read();
|
reader.read();
|
||||||
|
NoColorArgument::apply();
|
||||||
} catch (const Failure &) {
|
} catch (const Failure &) {
|
||||||
|
NoColorArgument::apply();
|
||||||
if (!completionMode) {
|
if (!completionMode) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -1375,4 +1377,91 @@ HelpArgument::HelpArgument(ArgumentParser &parser)
|
||||||
* \brief The ConfigValueArgument class is an Argument where setCombinable() is true by default.
|
* \brief The ConfigValueArgument class is an Argument where setCombinable() is true by default.
|
||||||
* \sa ConfigValueArgument::ConfigValueArgument()
|
* \sa ConfigValueArgument::ConfigValueArgument()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \class NoColorArgument
|
||||||
|
* \brief The NoColorArgument class allows to specify whether use of escape codes or similar technique to provide formatted output
|
||||||
|
* on the terminal should be enabled/disabled.
|
||||||
|
*
|
||||||
|
* This argument will either prevent or explicitely allow the use of escape codes or similar technique to provide formatted output
|
||||||
|
* on the terminal. More explicitly, the argument will always allow to negate the default value of EscapeCodes::enabled which can be
|
||||||
|
* configured at build time by setting the CMake variable ENABLE_ESCAPE_CODES_BY_DEFAULT.
|
||||||
|
*
|
||||||
|
* \remarks
|
||||||
|
* - Only the first instance is considered for actually altering the value of EscapeCodes::enabled so it makes no sense to
|
||||||
|
* instantiate this class multiple times.
|
||||||
|
* - It is ensure that EscapeCodes::enabled will be set before any callback functions are invoked and even in the error case (if
|
||||||
|
* the error doesn't prevent the argument from being detected). Hence this feature is implemented via NoColorArgument::apply()
|
||||||
|
* rather than the usual callback mechanism.
|
||||||
|
*
|
||||||
|
* \sa NoColorArgument::NoColorArgument(), EscapeCodes::enabled
|
||||||
|
*/
|
||||||
|
|
||||||
|
NoColorArgument *NoColorArgument::s_instance = nullptr;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Constructs a new NoColorArgument argument.
|
||||||
|
* \remarks This will also set EscapeCodes::enabled to the value of the environment variable ENABLE_ESCAPE_CODES.
|
||||||
|
*/
|
||||||
|
NoColorArgument::NoColorArgument()
|
||||||
|
#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
|
||||||
|
: Argument("no-color", '\0', "disables formatted/colorized output")
|
||||||
|
#else
|
||||||
|
: Argument("enable-color", '\0', "enables formatted/colorized output")
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
setCombinable(true);
|
||||||
|
|
||||||
|
if (s_instance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s_instance = this;
|
||||||
|
|
||||||
|
// set the environmentvariable: note that this is not directly used and just assigned for printing help
|
||||||
|
setEnvironmentVariable("ENABLE_ESCAPE_CODES");
|
||||||
|
|
||||||
|
// default-initialize EscapeCodes::enabled from environment variable
|
||||||
|
const char *envValue = getenv(environmentVariable());
|
||||||
|
if (!envValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (; *envValue; ++envValue) {
|
||||||
|
switch (*envValue) {
|
||||||
|
case '0':
|
||||||
|
case ' ':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// enable escape codes if ENABLE_ESCAPE_CODES contains anything else than spaces or zeros
|
||||||
|
EscapeCodes::enabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// disable escape codes if ENABLE_ESCAPE_CODES is empty or only contains spaces and zeros
|
||||||
|
EscapeCodes::enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Destroys the object.
|
||||||
|
*/
|
||||||
|
NoColorArgument::~NoColorArgument()
|
||||||
|
{
|
||||||
|
if (s_instance == this) {
|
||||||
|
s_instance = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Sets EscapeCodes::enabled according to the presense of the first instantiation of NoColorArgument.
|
||||||
|
*/
|
||||||
|
void NoColorArgument::apply()
|
||||||
|
{
|
||||||
|
if (NoColorArgument::s_instance && NoColorArgument::s_instance->isPresent()) {
|
||||||
|
#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
|
||||||
|
EscapeCodes::enabled = false;
|
||||||
|
#else
|
||||||
|
EscapeCodes::enabled = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ApplicationUtilities
|
} // namespace ApplicationUtilities
|
||||||
|
|
|
@ -873,6 +873,17 @@ inline ConfigValueArgument::ConfigValueArgument(
|
||||||
setRequiredValueCount(valueNames.size());
|
setRequiredValueCount(valueNames.size());
|
||||||
setValueNames(valueNames);
|
setValueNames(valueNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CPP_UTILITIES_EXPORT NoColorArgument : public Argument {
|
||||||
|
public:
|
||||||
|
NoColorArgument();
|
||||||
|
~NoColorArgument();
|
||||||
|
static void apply();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static NoColorArgument *s_instance;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ApplicationUtilities
|
} // namespace ApplicationUtilities
|
||||||
|
|
||||||
#endif // APPLICATION_UTILITIES_ARGUMENTPARSER_H
|
#endif // APPLICATION_UTILITIES_ARGUMENTPARSER_H
|
||||||
|
|
|
@ -70,6 +70,12 @@ None of these are enabled or set by default, unless stated otherwise.
|
||||||
* coverage report is stored in build directory
|
* coverage report is stored in build directory
|
||||||
* `ENABLE_INSTALL_TARGETS=ON/OFF`: enables creation of install targets (enabled
|
* `ENABLE_INSTALL_TARGETS=ON/OFF`: enables creation of install targets (enabled
|
||||||
by default)
|
by default)
|
||||||
|
* `ENABLE_ESCAPE_CODES_BY_DEAULT`: enables use of escape codes for formatted
|
||||||
|
output by default
|
||||||
|
* enabled by default
|
||||||
|
* see ApplicationUtilities::NoColorArgument and EscapeCodes::enabled
|
||||||
|
* has to be set when building `c++utilities`; projects using that build of
|
||||||
|
`c++utilities` will then use this default
|
||||||
|
|
||||||
#### Variables for specifying location of 3rd party dependencies
|
#### Variables for specifying location of 3rd party dependencies
|
||||||
The build script tries to find the required dependencies at standard loctions
|
The build script tries to find the required dependencies at standard loctions
|
||||||
|
|
|
@ -1,7 +1,30 @@
|
||||||
#include "./ansiescapecodes.h"
|
#include "./ansiescapecodes.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Encapsulates functions for formatted terminal output using ANSI escape codes.
|
||||||
|
*/
|
||||||
namespace EscapeCodes {
|
namespace EscapeCodes {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Controls whether the functions inside the EscapeCodes namespace actually make use of escape codes.
|
||||||
|
*
|
||||||
|
* This allows to disable use of escape codes when not appropriate.
|
||||||
|
*
|
||||||
|
* The default value can be configured at build time by setting the CMake variable ENABLE_ESCAPE_CODES_BY_DEFAULT.
|
||||||
|
* The "default for the default" is true.
|
||||||
|
* However, the default is overridden with the value of the environment variable ENABLE_ESCAPE_CODES when instantiating
|
||||||
|
* an ApplicationUtilities::NoColorArgument (if ENABLE_ESCAPE_CODES is present).
|
||||||
|
*
|
||||||
|
* \sa ApplicationUtilities::NoColorArgument
|
||||||
|
*/
|
||||||
|
bool enabled =
|
||||||
|
#ifdef CPP_UTILITIES_ESCAPE_CODES_ENABLED_BY_DEFAULT
|
||||||
|
true
|
||||||
|
#else
|
||||||
|
false
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Prints the specified \a phrase.
|
* \brief Prints the specified \a phrase.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,12 +6,10 @@
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Encapsulates functions for formatted terminal output using ANSI escape codes.
|
|
||||||
* \remarks The functions haven't been tested yet and are still experimental. API/ABI might change in next minor release.
|
|
||||||
*/
|
|
||||||
namespace EscapeCodes {
|
namespace EscapeCodes {
|
||||||
|
|
||||||
|
extern CPP_UTILITIES_EXPORT bool enabled;
|
||||||
|
|
||||||
enum class Color : char { Black = '0', Red, Green, Yellow, Blue, Purple, Cyan, White };
|
enum class Color : char { Black = '0', Red, Green, Yellow, Blue, Purple, Cyan, White };
|
||||||
|
|
||||||
enum class ColorContext : char { Foreground = '3', Background = '4' };
|
enum class ColorContext : char { Foreground = '3', Background = '4' };
|
||||||
|
@ -31,54 +29,75 @@ enum class Direction : char { Up = 'A', Down = 'B', Forward = 'C', Backward = 'D
|
||||||
|
|
||||||
inline void setStyle(std::ostream &stream, TextAttribute displayAttribute = TextAttribute::Reset)
|
inline void setStyle(std::ostream &stream, TextAttribute displayAttribute = TextAttribute::Reset)
|
||||||
{
|
{
|
||||||
stream << '\e' << '[' << static_cast<char>(displayAttribute) << 'm';
|
if (enabled) {
|
||||||
|
stream << '\e' << '[' << static_cast<char>(displayAttribute) << 'm';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setStyle(
|
inline void setStyle(
|
||||||
std::ostream &stream, Color color, ColorContext context = ColorContext::Foreground, TextAttribute displayAttribute = TextAttribute::Reset)
|
std::ostream &stream, Color color, ColorContext context = ColorContext::Foreground, TextAttribute displayAttribute = TextAttribute::Reset)
|
||||||
{
|
{
|
||||||
stream << '\e' << '[' << static_cast<char>(displayAttribute) << ';' << static_cast<char>(context) << static_cast<char>(color) << 'm';
|
if (enabled) {
|
||||||
|
stream << '\e' << '[' << static_cast<char>(displayAttribute) << ';' << static_cast<char>(context) << static_cast<char>(color) << 'm';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setStyle(std::ostream &stream, Color foregroundColor, Color backgroundColor, TextAttribute displayAttribute = TextAttribute::Reset)
|
inline void setStyle(std::ostream &stream, Color foregroundColor, Color backgroundColor, TextAttribute displayAttribute = TextAttribute::Reset)
|
||||||
{
|
{
|
||||||
stream << '\e' << '[' << static_cast<char>(displayAttribute) << ';' << static_cast<char>(ColorContext::Foreground)
|
if (enabled) {
|
||||||
<< static_cast<char>(foregroundColor) << ';' << static_cast<char>(ColorContext::Foreground) << static_cast<char>(backgroundColor) << 'm';
|
stream << '\e' << '[' << static_cast<char>(displayAttribute) << ';' << static_cast<char>(ColorContext::Foreground)
|
||||||
|
<< static_cast<char>(foregroundColor) << ';' << static_cast<char>(ColorContext::Foreground) << static_cast<char>(backgroundColor)
|
||||||
|
<< 'm';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void resetStyle(std::ostream &stream)
|
inline void resetStyle(std::ostream &stream)
|
||||||
{
|
{
|
||||||
stream << '\e' << '[' << static_cast<char>(TextAttribute::Reset) << 'm';
|
if (enabled) {
|
||||||
|
stream << '\e' << '[' << static_cast<char>(TextAttribute::Reset) << 'm';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setCursor(std::ostream &stream, unsigned int row = 0, unsigned int col = 0)
|
inline void setCursor(std::ostream &stream, unsigned int row = 0, unsigned int col = 0)
|
||||||
{
|
{
|
||||||
stream << '\e' << '[' << row << ';' << col << 'H';
|
if (enabled) {
|
||||||
|
stream << '\e' << '[' << row << ';' << col << 'H';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void moveCursor(std::ostream &stream, unsigned int cells, Direction direction)
|
inline void moveCursor(std::ostream &stream, unsigned int cells, Direction direction)
|
||||||
{
|
{
|
||||||
stream << '\e' << '[' << cells << static_cast<char>(direction);
|
if (enabled) {
|
||||||
|
stream << '\e' << '[' << cells << static_cast<char>(direction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void saveCursor(std::ostream &stream)
|
inline void saveCursor(std::ostream &stream)
|
||||||
{
|
{
|
||||||
stream << "\e[s";
|
if (enabled) {
|
||||||
|
stream << "\e[s";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void restoreCursor(std::ostream &stream)
|
inline void restoreCursor(std::ostream &stream)
|
||||||
{
|
{
|
||||||
stream << "\e[u";
|
if (enabled) {
|
||||||
|
stream << "\e[u";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void eraseDisplay(std::ostream &stream)
|
inline void eraseDisplay(std::ostream &stream)
|
||||||
{
|
{
|
||||||
stream << "\e[2J";
|
if (enabled) {
|
||||||
|
stream << "\e[2J";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void eraseLine(std::ostream &stream)
|
inline void eraseLine(std::ostream &stream)
|
||||||
{
|
{
|
||||||
stream << "\33[2K";
|
if (enabled) {
|
||||||
|
stream << "\33[2K";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::ostream &operator<<(std::ostream &stream, TextAttribute displayAttribute)
|
inline std::ostream &operator<<(std::ostream &stream, TextAttribute displayAttribute)
|
||||||
|
|
Loading…
Reference in New Issue