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