Riyyi
2 years ago
4 changed files with 368 additions and 182 deletions
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#include <cstdint> // uint8_t |
||||
#include <cstdio> // FILE, fputs, stdout |
||||
#include <sstream> // stringstream |
||||
#include <string> |
||||
#include <string_view> |
||||
|
||||
#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<Emphasis>(static_cast<uint8_t>(m_emphasis) | static_cast<uint8_t>(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<uint8_t>(lhs) & static_cast<uint8_t>(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<uint8_t>(style.foregroundColor())); |
||||
} |
||||
|
||||
if (hasBackground) { |
||||
if (hasForeground) { |
||||
stream.write(";", 1); |
||||
} |
||||
stream << format("{}", static_cast<uint8_t>(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
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <cstddef> // size_t |
||||
#include <cstdint> // uint8_t |
||||
#include <cstdio> // FILE, stdout |
||||
#include <sstream> // stringstream |
||||
#include <string_view> |
||||
|
||||
#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<typename... Parameters> |
||||
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<typename... Parameters> |
||||
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<size_t N, typename... Parameters> |
||||
void print(const TextStyle& style, const char (&format)[N], const Parameters&... parameters) |
||||
{ |
||||
VariadicParameters variadicParameters { parameters... }; |
||||
coloredVariadicPrint(stdout, style, { format, N - 1 }, variadicParameters); |
||||
} |
||||
|
||||
template<size_t N, typename... Parameters> |
||||
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<typename T> |
||||
const ColorPrintOperatorStyle& operator<<(const ColorPrintOperatorStyle& colorPrintOperatorStyle, const T& value) |
||||
{ |
||||
_format(const_cast<ColorPrintOperatorStyle&>(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
|
Loading…
Reference in new issue