diff --git a/src/util/format/color.cpp b/src/util/format/color.cpp new file mode 100644 index 0000000..970dbfd --- /dev/null +++ b/src/util/format/color.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2022 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // uint8_t +#include // FILE, fputs, stdout +#include // stringstream +#include +#include + +#include "util/format/color.h" +#include "util/format/format.h" +#include "util/meta/assert.h" + +namespace Util::Format { + +TextStyle::TextStyle(Emphasis emphasis) + : m_emphasis(emphasis) +{ +} + +TextStyle::TextStyle(bool isForeground, TerminalColor color) +{ + if (isForeground) { + m_foregroundColor = color; + } + else { + m_backgroundColor = color; + } +} + +// ----------------------------------------- + +TextStyle& TextStyle::operator|=(const TextStyle& rhs) +{ + if (m_foregroundColor == TerminalColor::None) { + m_foregroundColor = rhs.m_foregroundColor; + } + else { + VERIFY(rhs.m_foregroundColor == TerminalColor::None, "can't OR a terminal color"); + } + + if (m_backgroundColor == TerminalColor::None) { + m_backgroundColor = rhs.m_backgroundColor; + } + else { + VERIFY(rhs.m_backgroundColor == TerminalColor::None, "can't OR a terminal color"); + } + + m_emphasis = static_cast(static_cast(m_emphasis) | static_cast(rhs.m_emphasis)); + + return *this; +} + +TextStyle fg(TerminalColor foreground) +{ + return TextStyle(true, foreground); +} + +TextStyle bg(TerminalColor background) +{ + return TextStyle(false, background); +} + +TextStyle operator|(TextStyle lhs, const TextStyle& rhs) +{ + return lhs |= rhs; +} + +TextStyle operator|(Emphasis lhs, Emphasis rhs) +{ + return TextStyle { lhs } | rhs; +} + +bool operator&(Emphasis lhs, Emphasis rhs) +{ + return static_cast(lhs) & static_cast(rhs); +} + +// ----------------------------------------- + +void setDisplayAttributes(std::stringstream& stream, const TextStyle& style) +{ + bool hasForeground = style.foregroundColor() != TerminalColor::None; + bool hasBackground = style.backgroundColor() != TerminalColor::None; + bool hasEmphasis = style.emphasis() != Emphasis::None; + if (!hasForeground && !hasBackground && !hasEmphasis) { + return; + } + + stream.write("\033[", 2); + + if (hasForeground) { + stream << format("{}", static_cast(style.foregroundColor())); + } + + if (hasBackground) { + if (hasForeground) { + stream.write(";", 1); + } + stream << format("{}", static_cast(style.backgroundColor()) + 10); + } + + stream.write("m", 1); + + if (hasEmphasis) { + +#define ESCAPE_ATTRIBUTE(escape, attribute) \ + if (style.emphasis() & escape) { \ + stream.write("\033[", 2); \ + stream.write(attribute, 1); \ + stream.write("m", 1); \ + } + + ESCAPE_ATTRIBUTE(Emphasis::Bold, "1"); + ESCAPE_ATTRIBUTE(Emphasis::Faint, "2"); + ESCAPE_ATTRIBUTE(Emphasis::Italic, "3"); + ESCAPE_ATTRIBUTE(Emphasis::Underline, "4"); + ESCAPE_ATTRIBUTE(Emphasis::Blink, "5"); + ESCAPE_ATTRIBUTE(Emphasis::Reverse, "7"); + ESCAPE_ATTRIBUTE(Emphasis::Conceal, "8"); + ESCAPE_ATTRIBUTE(Emphasis::Strikethrough, "9"); + } +} + +void coloredVariadicFormat(std::stringstream& stream, const TextStyle& style, std::string_view format, TypeErasedParameters& parameters) +{ + setDisplayAttributes(stream, style); + variadicFormat(stream, format, parameters); + stream.write("\033[0m", 4); +} + +// ----------------------------------------- + +void coloredVariadicPrint(FILE* file, const TextStyle& style, std::string_view format, TypeErasedParameters& parameters) +{ + std::stringstream stream; + setDisplayAttributes(stream, style); + variadicFormat(stream, format, parameters); + stream.write("\033[0m", 4); + + std::string string = stream.str(); + fputs(string.c_str(), file); +} + +// ----------------------------------------- + +ColorPrintOperatorStyle::ColorPrintOperatorStyle(FILE* file, const TextStyle& style) + : m_file(file) + , m_style(style) + , m_stream() + , m_builder(m_stream) +{ + setDisplayAttributes(m_stream, style); +} + +ColorPrintOperatorStyle::~ColorPrintOperatorStyle() +{ + m_stream.write("\033[0m", 4); + std::string string = m_stream.str(); + fputs(string.c_str(), m_file); +} + +ColorPrintOperatorStyle print(const TextStyle& style) +{ + return ColorPrintOperatorStyle(stdout, style); +} + +ColorPrintOperatorStyle print(FILE* file, const TextStyle& style) +{ + return ColorPrintOperatorStyle(file, style); +} + +} // namespace Util::Format diff --git a/src/util/format/color.h b/src/util/format/color.h new file mode 100644 index 0000000..71f9c68 --- /dev/null +++ b/src/util/format/color.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2022 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include // size_t +#include // uint8_t +#include // FILE, stdout +#include // stringstream +#include + +#include "util/format/format.h" + +namespace Util::Format { + +enum class TerminalColor : uint8_t { + None = 0, + Black = 30, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + White, + BrightBlack = 90, + BrightRed, + BrightGreen, + BrightYellow, + BrightBlue, + BrightMagenta, + BrightCyan, + BrightWhite +}; + +// Bit field +enum class Emphasis : uint8_t { + None = 0, // Attribute 0 + Bold = 1, // Attribute 1 + Faint = 1 << 1, // Attribute 2 + Italic = 1 << 2, // Attribute 3 + Underline = 1 << 3, // Attribute 4 + Blink = 1 << 4, // Attribute 5 + Reverse = 1 << 5, // Attribute 7 + Conceal = 1 << 6, // Attribute 8 + Strikethrough = 1 << 7, // Attribute 9 +}; + +class TextStyle { +private: + friend TextStyle fg(TerminalColor foreground); + friend TextStyle bg(TerminalColor background); + +public: + TextStyle(Emphasis emphasis); + + // Operator pipe equal, reads the same way as += + TextStyle& operator|=(const TextStyle& rhs); + + TerminalColor foregroundColor() const { return m_foregroundColor; } + TerminalColor backgroundColor() const { return m_backgroundColor; } + Emphasis emphasis() const { return m_emphasis; } + +private: + TextStyle(bool isForeground, TerminalColor color); + + TerminalColor m_foregroundColor { TerminalColor::None }; + TerminalColor m_backgroundColor { TerminalColor::None }; + Emphasis m_emphasis { 0 }; +}; + +TextStyle fg(TerminalColor foreground); +TextStyle bg(TerminalColor background); +TextStyle operator|(TextStyle lhs, const TextStyle& rhs); +TextStyle operator|(Emphasis lhs, Emphasis rhs); +bool operator&(Emphasis lhs, Emphasis rhs); + +// ----------------------------------------- + +void coloredVariadicFormat(std::stringstream& stream, const TextStyle& style, std::string_view format, TypeErasedParameters& parameters); + +template +std::string format(const TextStyle& style, std::string_view format, const Parameters&... parameters) +{ + std::stringstream stream; + VariadicParameters variadicParameters { parameters... }; + coloredVariadicFormat(stream, style, format, variadicParameters); + return stream.str(); +} + +template +void formatTo(std::string& output, const TextStyle& style, std::string_view format, const Parameters&... parameters) +{ + std::stringstream stream; + VariadicParameters variadicParameters { parameters... }; + coloredVariadicFormat(stream, style, format, variadicParameters); + output += stream.str(); +} + +// ----------------------------------------- + +void coloredVariadicPrint(FILE* file, const TextStyle& style, std::string_view format, TypeErasedParameters& parameters); + +template +void print(const TextStyle& style, const char (&format)[N], const Parameters&... parameters) +{ + VariadicParameters variadicParameters { parameters... }; + coloredVariadicPrint(stdout, style, { format, N - 1 }, variadicParameters); +} + +template +void print(FILE* file, const TextStyle& style, const char (&format)[N], const Parameters&... parameters) +{ + VariadicParameters variadicParameters { parameters... }; + coloredVariadicPrint(file, style, { format, N - 1 }, variadicParameters); +} + +// ----------------------------------------- + +class ColorPrintOperatorStyle { +public: + ColorPrintOperatorStyle(FILE* file, const TextStyle& style); + virtual ~ColorPrintOperatorStyle(); + + Builder& builder() { return m_builder; } + +private: + FILE* m_file; + TextStyle m_style; + + std::stringstream m_stream; + Builder m_builder; +}; + +template +const ColorPrintOperatorStyle& operator<<(const ColorPrintOperatorStyle& colorPrintOperatorStyle, const T& value) +{ + _format(const_cast(colorPrintOperatorStyle).builder(), value); + return colorPrintOperatorStyle; +} + +ColorPrintOperatorStyle print(const TextStyle& style); +ColorPrintOperatorStyle print(FILE* file, const TextStyle& style); + +} // namespace Util::Format + +namespace Util { + +using Util::Format::format; +using Util::Format::formatTo; +using Util::Format::print; + +} // namespace Util diff --git a/src/util/format/print.cpp b/src/util/format/print.cpp index 2fca2d3..e160d9b 100644 --- a/src/util/format/print.cpp +++ b/src/util/format/print.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: MIT */ -#include // FILE, fputs +#include // FILE, fputs, stdout, stderr #include // setprecision #include // defaultfloat, fixed #include // stringstream @@ -15,47 +15,9 @@ namespace Util::Format { -void printTimeElapsedAndTypePrefix(std::stringstream& stream, Type type, bool bold) -{ - stream << std::fixed << std::setprecision(3) - << s_timer.elapsedNanoseconds() / 1000000000.0 << "s " - << std::defaultfloat << std::setprecision(6); - - stream << "[\033["; - if (bold) { - stream << "1"; - } - switch (type) { - case Type::None: - stream << ";35mdebug"; - break; - case Type::Info: - stream << ";34minfo"; - break; - case Type::Warn: - stream << ";33mwarn"; - break; - case Type::Critical: - stream << ";31mcritical"; - break; - case Type::Success: - stream << ";32msuccess"; - break; - case Type::Comment: - stream << "mcomment"; - break; - default: - break; - }; - stream << "\033[0m] "; -} - -void prettyVariadicFormat(FILE* file, Type type, bool bold, std::string_view format, TypeErasedParameters& parameters) +void variadicPrint(FILE* file, std::string_view format, TypeErasedParameters& parameters) { std::stringstream stream; - - printTimeElapsedAndTypePrefix(stream, type, bold); - variadicFormat(stream, format, parameters); std::string string = stream.str(); @@ -64,80 +26,27 @@ void prettyVariadicFormat(FILE* file, Type type, bool bold, std::string_view for // ----------------------------------------- -FormatPrint::FormatPrint(FILE* file, Type type, bool bold) +PrintOperatorStyle::PrintOperatorStyle(FILE* file) : m_file(file) - , m_type(type) - , m_bold(bold) , m_stream() , m_builder(m_stream) { - printTimeElapsedAndTypePrefix(m_stream, type, bold); } -FormatPrint::~FormatPrint() +PrintOperatorStyle::~PrintOperatorStyle() { std::string string = m_stream.str(); fputs(string.c_str(), m_file); } -FormatPrint dbg() -{ - return FormatPrint(stdout, Type::None, false); -} - -FormatPrint dbgb() -{ - return FormatPrint(stdout, Type::None, true); -} - -FormatPrint info() -{ - return FormatPrint(stdout, Type::Info, false); -} - -FormatPrint infob() -{ - return FormatPrint(stdout, Type::Info, true); -} - -FormatPrint warn() -{ - return FormatPrint(stdout, Type::Warn, false); -} - -FormatPrint warnb() -{ - return FormatPrint(stdout, Type::Warn, true); -} - -FormatPrint critical() -{ - return FormatPrint(stderr, Type::Critical, false); -} - -FormatPrint criticalb() -{ - return FormatPrint(stderr, Type::Critical, true); -} - -FormatPrint success() -{ - return FormatPrint(stdout, Type::Success, false); -} - -FormatPrint successb() -{ - return FormatPrint(stdout, Type::Success, true); -} - -FormatPrint comment() +PrintOperatorStyle print() { - return FormatPrint(stdout, Type::Comment, false); + return PrintOperatorStyle(stdout); } -FormatPrint commentb() +PrintOperatorStyle print(FILE* file) { - return FormatPrint(stdout, Type::Comment, true); + return PrintOperatorStyle(file); } } // namespace Util::Format diff --git a/src/util/format/print.h b/src/util/format/print.h index a4372da..0be217c 100644 --- a/src/util/format/print.h +++ b/src/util/format/print.h @@ -4,90 +4,59 @@ * SPDX-License-Identifier: MIT */ -#include // FILE +#pragma once + +#include // uint8_t +#include // FILE, stdout +#include // stringstream +#include #include "util/format/format.h" #include "util/timer.h" namespace Util::Format { -static Util::Timer s_timer; +void variadicPrint(FILE* file, std::string_view format, TypeErasedParameters& parameters); -enum class Type { - None, // Foreground - Info, // Blue - Warn, // Yellow - Critical, // Red - Success, // Green - Comment, // White -}; +template +void print(const char (&format)[N], const Parameters&... parameters) +{ + VariadicParameters variadicParameters { parameters... }; + variadicPrint(stdout, { format, N - 1 }, variadicParameters); +} -void prettyVariadicFormat(FILE* file, Type type, bool bold, std::string_view format, TypeErasedParameters& parameters); - -#define FORMAT_FUNCTION(name, type, bold) \ - template \ - void name(const char(&format)[N], const Parameters&... parameters) \ - { \ - VariadicParameters variadicParameters { parameters... }; \ - prettyVariadicFormat(stdout, Type::type, bold, { format, N - 1 }, variadicParameters); \ - } \ - template \ - void name(FILE* file, const char(&format)[N], const Parameters&... parameters) \ - { \ - VariadicParameters variadicParameters { parameters... }; \ - prettyVariadicFormat(file, Type::type, bold, { format, N - 1 }, variadicParameters); \ - } - -FORMAT_FUNCTION(dbgln, None, false); -FORMAT_FUNCTION(dbgbln, None, true); -FORMAT_FUNCTION(infoln, Info, false); -FORMAT_FUNCTION(infobln, Info, true); -FORMAT_FUNCTION(warnln, Warn, false); -FORMAT_FUNCTION(warnbln, Warn, true); -FORMAT_FUNCTION(criticalln, Critical, false); -FORMAT_FUNCTION(criticalbln, Critical, true); -FORMAT_FUNCTION(successln, Success, false); -FORMAT_FUNCTION(successbln, Success, true); -FORMAT_FUNCTION(commentln, Comment, false); -FORMAT_FUNCTION(commentbln, Comment, true); +template +void print(FILE* file, const char (&format)[N], const Parameters&... parameters) +{ + VariadicParameters variadicParameters { parameters... }; + variadicPrint(file, { format, N - 1 }, variadicParameters); +} // ----------------------------------------- -class FormatPrint { +class PrintOperatorStyle { public: - FormatPrint(FILE* file, Type type, bool bold); - virtual ~FormatPrint(); + PrintOperatorStyle(FILE* file); + virtual ~PrintOperatorStyle(); Builder& builder() { return m_builder; } private: FILE* m_file; - Type m_type; - bool m_bold; std::stringstream m_stream; Builder m_builder; }; template -const FormatPrint& operator<<(const FormatPrint& formatPrint, const T& value) +const PrintOperatorStyle& operator<<(const PrintOperatorStyle& printOperatorStyle, const T& value) { - _format(const_cast(formatPrint).builder(), value); - return formatPrint; + _format(const_cast(printOperatorStyle).builder(), value); + return printOperatorStyle; } -FormatPrint dbg(); -FormatPrint dbgb(); -FormatPrint info(); -FormatPrint infob(); -FormatPrint warn(); -FormatPrint warnb(); -FormatPrint critical(); -FormatPrint criticalb(); -FormatPrint success(); -FormatPrint successb(); -FormatPrint comment(); -FormatPrint commentb(); +PrintOperatorStyle print(); +PrintOperatorStyle print(FILE* file); // ----------------------------------------- @@ -95,30 +64,6 @@ FormatPrint commentb(); namespace Util { -using Util::Format::commentbln; -using Util::Format::commentln; -using Util::Format::criticalbln; -using Util::Format::criticalln; -using Util::Format::dbgbln; -using Util::Format::dbgln; -using Util::Format::infobln; -using Util::Format::infoln; -using Util::Format::successbln; -using Util::Format::successln; -using Util::Format::warnbln; -using Util::Format::warnln; - -using Util::Format::comment; -using Util::Format::commentb; -using Util::Format::critical; -using Util::Format::criticalb; -using Util::Format::dbg; -using Util::Format::dbgb; -using Util::Format::info; -using Util::Format::infob; -using Util::Format::success; -using Util::Format::successb; -using Util::Format::warn; -using Util::Format::warnb; +using Util::Format::print; } // namespace Util