Make use of escape codes configurable

This commit is contained in:
Martchus 2017-10-17 00:00:46 +02:00
parent 6933b7b33e
commit 9829dbe727
6 changed files with 175 additions and 15 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.
*/ */

View File

@ -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)