Make use of escape codes configurable

experiment/srcref_basic_cfg
Martchus 5 years ago
parent 6933b7b33e
commit 9829dbe727
  1. 12
      CMakeLists.txt
  2. 89
      application/argumentparser.cpp
  3. 11
      application/argumentparser.h
  4. 6
      doc/buildvariables.md
  5. 23
      io/ansiescapecodes.cpp
  6. 49
      io/ansiescapecodes.h

@ -147,6 +147,18 @@ if(FORCE_UTF8_CODEPAGE)
list(APPEND META_PRIVATE_COMPILE_DEFINITIONS ${META_PROJECT_VARNAME}_FORCE_UTF8_CODEPAGE)
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(BasicConfig)
include(WindowsResources)

@ -782,7 +782,9 @@ void ArgumentParser::readArgs(int argc, const char *const *argv)
completionMode);
try {
reader.read();
NoColorArgument::apply();
} catch (const Failure &) {
NoColorArgument::apply();
if (!completionMode) {
throw;
}
@ -1375,4 +1377,91 @@ HelpArgument::HelpArgument(ArgumentParser &parser)
* \brief The ConfigValueArgument class is an Argument where setCombinable() is true by default.
* \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

@ -873,6 +873,17 @@ inline ConfigValueArgument::ConfigValueArgument(
setRequiredValueCount(valueNames.size());
setValueNames(valueNames);
}
class CPP_UTILITIES_EXPORT NoColorArgument : public Argument {
public:
NoColorArgument();
~NoColorArgument();
static void apply();
private:
static NoColorArgument *s_instance;
};
} // namespace ApplicationUtilities
#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
* `ENABLE_INSTALL_TARGETS=ON/OFF`: enables creation of install targets (enabled
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
The build script tries to find the required dependencies at standard loctions

@ -1,7 +1,30 @@
#include "./ansiescapecodes.h"
/*!
* \brief Encapsulates functions for formatted terminal output using ANSI escape codes.
*/
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.
*/

@ -6,12 +6,10 @@
#include <ostream>
#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 {
extern CPP_UTILITIES_EXPORT bool enabled;
enum class Color : char { Black = '0', Red, Green, Yellow, Blue, Purple, Cyan, White };
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)
{
stream << '\e' << '[' << static_cast<char>(displayAttribute) << 'm';
if (enabled) {
stream << '\e' << '[' << static_cast<char>(displayAttribute) << 'm';
}
}
inline void setStyle(
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)
{
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';
if (enabled) {
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)
{
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)
{
stream << '\e' << '[' << row << ';' << col << 'H';
if (enabled) {
stream << '\e' << '[' << row << ';' << col << 'H';
}
}
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)
{
stream << "\e[s";
if (enabled) {
stream << "\e[s";
}
}
inline void restoreCursor(std::ostream &stream)
{
stream << "\e[u";
if (enabled) {
stream << "\e[u";
}
}
inline void eraseDisplay(std::ostream &stream)
{
stream << "\e[2J";
if (enabled) {
stream << "\e[2J";
}
}
inline void eraseLine(std::ostream &stream)
{
stream << "\33[2K";
if (enabled) {
stream << "\33[2K";
}
}
inline std::ostream &operator<<(std::ostream &stream, TextAttribute displayAttribute)

Loading…
Cancel
Save