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