main
Brett 2025-03-05 01:55:14 -05:00
parent fcbc6b947d
commit 08e19c067e
5 changed files with 382 additions and 198 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake) include(cmake/color.cmake)
set(BLT_VERSION 5.1.10) set(BLT_VERSION 5.1.11)
set(BLT_TARGET BLT) set(BLT_TARGET BLT)

View File

@ -19,7 +19,10 @@
#ifndef BLT_LOGGING_COLORS_H #ifndef BLT_LOGGING_COLORS_H
#define BLT_LOGGING_COLORS_H #define BLT_LOGGING_COLORS_H
#include <array>
#include <stdexcept>
#include <string> #include <string>
#include <variant>
#include <blt/std/types.h> #include <blt/std/types.h>
#define BLT_ANSI_ESCAPE "\x1B" #define BLT_ANSI_ESCAPE "\x1B"
@ -30,155 +33,308 @@
// https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 // https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
namespace blt::logging::ansi namespace blt::logging::ansi
{ {
namespace color namespace color
{ {
enum class color_mode : i32 inline std::array<u8, 10> reset_sequences = {0, 22, 22, 23, 24, 25, 26, 27, 28, 29};
{
RESET_ALL = 0,
BOLD = 1,
DIM = 2,
ITALIC = 3,
UNDERLINE = 4,
BLINK = 5,
REVERSE = 7,
HIDDEN = 8,
STRIKE_THROUGH = 9,
};
enum class color8 : i32 enum class color_mode : u8
{ {
BLACK = 0, RESET_ALL = 0,
RED = 1, BOLD = 1,
GREEN = 2, DIM = 2,
YELLOW = 3, ITALIC = 3,
BLUE = 4, UNDERLINE = 4,
MAGENTA = 5, BLINK = 5,
CYAN = 6, REVERSE = 7,
WHITE = 7, HIDDEN = 8,
DEFAULT = 9 STRIKE_THROUGH = 9, };
};
enum class color8_bright : i32 enum class color8 : u8
{ {
BLACK = 0, BLACK = 0,
RED = 1, RED = 1,
GREEN = 2, GREEN = 2,
YELLOW = 3, YELLOW = 3,
BLUE = 4, BLUE = 4,
MAGENTA = 5, MAGENTA = 5,
CYAN = 6, CYAN = 6,
WHITE = 7, WHITE = 7,
}; DEFAULT = 9
};
namespace detail enum class color8_bright : u8
{ {
template <typename T> BLACK = 0,
struct color_converter RED = 1,
{ GREEN = 2,
}; YELLOW = 3,
BLUE = 4,
MAGENTA = 5,
CYAN = 6,
WHITE = 7, };
template <> struct rgb_t
struct color_converter<color8> {
{ u8 r, g, b;
static std::string to_string(color8 color, const bool background) };
{
return (background ? "4" : "3") + std::to_string(static_cast<i32>(color));
}
};
template <> struct color256
struct color_converter<color8_bright> {
{ explicit color256(u8 index) : color(index)
static std::string to_string(color8_bright color, const bool background) {}
{
return (background ? "10" : "9") + std::to_string(static_cast<i32>(color));
}
};
}
}
namespace general color256(const u8 r, const u8 g, const u8 b) : color(rgb_t{r, g, b})
{ {
inline const std::string bell = "\x07"; if (r > 5)
inline const std::string bs = "\x08"; throw std::invalid_argument("r must be between 0 and 5");
inline const std::string horizontal_tab = "\x09"; if (g > 5)
inline const std::string linefeed = "\x0A"; throw std::invalid_argument("g must be between 0 and 5");
inline const std::string vertical_tab = "\x0B"; if (b > 5)
inline const std::string form_feed = "\x0C"; throw std::invalid_argument("b must be between 0 and 5");
inline const std::string carriage_return = "\x0D"; }
inline const std::string escape = BLT_ANSI_ESCAPE;
inline const std::string del = "\0x7F";
inline const std::string csi = BLT_ANSI_CSI;
inline const std::string dsc = BLT_ANSI_DSC;
inline const std::string osc = BLT_ANSI_OSC;
}
namespace cursor [[nodiscard]] u8 index() const
{ {
inline const std::string home = BLT_ANSI_CSI "H"; if (std::holds_alternative<u8>(color))
return std::get<u8>(color);
const auto [r, g, b] = std::get<rgb_t>(color);
return (r * 36) + (g * 6) + b + 16;
}
template <bool UseH> private:
inline std::string move_to(i64 line, i64 column) std::variant<u8, rgb_t> color;
{ };
if constexpr (UseH)
return std::string(BLT_ANSI_CSI) + std::to_string(line) + ";" + std::to_string(column) + "H";
else
return std::string(BLT_ANSI_CSI) + std::to_string(line) + ";" + std::to_string(column) + "f";
}
inline std::string move_up(i64 lines) struct color_rgb
{ {
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "A"; color_rgb(const u8 r, const u8 g, const u8 b) : color(rgb_t{r, g, b})
} {}
inline std::string move_down(i64 lines) rgb_t color;
{ };
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "B";
}
inline std::string move_right(i64 columns) namespace detail
{ {
return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "C"; template <typename T>
} struct color_holder
{
using value_type = T;
inline std::string move_left(i64 columns) T color;
{ bool alt = false;
return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "D"; };
}
inline std::string move_begin_down(i64 lines) template <typename T>
{ struct color_converter
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "E"; {};
}
inline std::string move_begin_up(i64 lines) template <>
{ struct color_converter<color8>
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "F"; {
} static std::string to_string(const color_holder<color8> color)
{
return (color.alt ? "4" : "3") + std::to_string(static_cast<u8>(color.color));
}
};
inline std::string move_to(i64 column) template <>
{ struct color_converter<color8_bright>
return std::string(BLT_ANSI_CSI) + std::to_string(column) + "G"; {
} static std::string to_string(const color_holder<color8_bright> color)
{
return (color.alt ? "10" : "9") + std::to_string(static_cast<u8>(color.color));
}
};
inline const std::string request_cursor_position = BLT_ANSI_CSI "6n"; template <>
inline const std::string move_up_one_line = BLT_ANSI_ESCAPE " M"; struct color_converter<color_mode>
inline const std::string save_cursor_position_dec = BLT_ANSI_ESCAPE " 7"; {
inline const std::string resotre_cursor_position_dec = BLT_ANSI_ESCAPE " 8"; static std::string to_string(const color_holder<color_mode> color)
inline const std::string save_cursor_position_sco = BLT_ANSI_CSI "s"; {
inline const std::string resotre_cursor_position_sco = BLT_ANSI_CSI "u"; return color.alt ? std::to_string(reset_sequences[static_cast<u8>(color.color)]) : std::to_string(static_cast<u8>(color.color));
} }
};
namespace erase template <>
{ struct color_converter<color256>
inline const std::string to_end_of_screen = BLT_ANSI_CSI "0J"; {
inline const std::string from_begin_of_screen = BLT_ANSI_CSI "1J"; static std::string to_string(const color_holder<color256> color)
inline const std::string entire_screen = BLT_ANSI_CSI "2J"; {
inline const std::string saved_lines = BLT_ANSI_CSI "3J"; return (color.alt ? "48;5;" : "38;5;") + std::to_string(color.color.index());
inline const std::string to_end_of_line = BLT_ANSI_CSI "0K"; }
inline const std::string from_begin_of_line = BLT_ANSI_CSI "1K"; };
inline const std::string entire_line = BLT_ANSI_CSI "2K";
} template <>
struct color_converter<color_rgb>
{
static std::string to_string(const color_holder<color_rgb> color)
{
return (color.alt ? "48;2;" : "38;2;") + std::to_string(color.color.color.r) + ";" + std::to_string(color.color.color.g) + ";" +
std::to_string(color.color.color.b);
}
};
template <typename T>
struct ensure_holder
{
static color_holder<T> make(const T& color)
{
return color_holder<T>{color, false};
}
};
template <typename T>
struct ensure_holder<color_holder<T>>
{
static color_holder<T> make(const color_holder<T>& color)
{
return color;
}
};
template<typename T>
struct decay
{
using type = std::decay_t<T>;
};
template<typename T>
struct decay<color_holder<T>>
{
using type = std::decay_t<T>;
};
template<typename T>
using decay_t = typename decay<T>::type;
}
template <typename T>
auto fg(const T& color)
{
return detail::color_holder<T>{color, false};
}
template <typename T>
auto bg(const T& color)
{
return detail::color_holder<T>{color, true};
}
template <typename... Args>
std::string build(const Args&... args)
{
std::string result = BLT_ANSI_CSI;
((result += detail::color_converter<detail::decay_t<Args>>::to_string(detail::ensure_holder<Args>::make(args)), result += ';'), ...);
return result.substr(0, result.size() - 1) + "m";
}
}
namespace general
{
inline const std::string bell = "\x07";
inline const std::string bs = "\x08";
inline const std::string horizontal_tab = "\x09";
inline const std::string linefeed = "\x0A";
inline const std::string vertical_tab = "\x0B";
inline const std::string form_feed = "\x0C";
inline const std::string carriage_return = "\x0D";
inline const std::string escape = BLT_ANSI_ESCAPE;
inline const std::string del = "\x7F";
inline const std::string csi = BLT_ANSI_CSI;
inline const std::string dsc = BLT_ANSI_DSC;
inline const std::string osc = BLT_ANSI_OSC;
}
namespace cursor
{
inline const std::string home = BLT_ANSI_CSI "H";
template <bool UseH>
std::string move_to(const i64 line, const i64 column)
{
if constexpr (UseH)
return std::string(BLT_ANSI_CSI) + std::to_string(line) + ";" + std::to_string(column) + "H";
else
return std::string(BLT_ANSI_CSI) + std::to_string(line) + ";" + std::to_string(column) + "f";
}
inline std::string move_up(const i64 lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "A";
}
inline std::string move_down(const i64 lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "B";
}
inline std::string move_right(const i64 columns)
{
return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "C";
}
inline std::string move_left(const i64 columns)
{
return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "D";
}
inline std::string move_begin_down(const i64 lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "E";
}
inline std::string move_begin_up(const i64 lines)
{
return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "F";
}
inline std::string move_to(const i64 column)
{
return std::string(BLT_ANSI_CSI) + std::to_string(column) + "G";
}
inline const std::string request_cursor_position = BLT_ANSI_CSI "6n";
inline const std::string move_up_one_line = BLT_ANSI_ESCAPE " M";
inline const std::string save_cursor_position_dec = BLT_ANSI_ESCAPE " 7";
inline const std::string restore_cursor_position_dec = BLT_ANSI_ESCAPE " 8";
inline const std::string save_cursor_position_sco = BLT_ANSI_CSI "s";
inline const std::string restore_cursor_position_sco = BLT_ANSI_CSI "u";
}
namespace erase
{
inline const std::string to_end_of_screen = BLT_ANSI_CSI "0J";
inline const std::string from_begin_of_screen = BLT_ANSI_CSI "1J";
inline const std::string entire_screen = BLT_ANSI_CSI "2J";
inline const std::string saved_lines = BLT_ANSI_CSI "3J";
inline const std::string to_end_of_line = BLT_ANSI_CSI "0K";
inline const std::string from_begin_of_line = BLT_ANSI_CSI "1K";
inline const std::string entire_line = BLT_ANSI_CSI "2K";
}
enum class mode : u8
{
mono40x25_text = 0,
color40x25_text = 1,
mono80x25_text = 2,
color80x25_text = 3,
color320x200_4color_graphics = 4,
mono320x200_graphics = 5,
mono640x200_graphics = 6,
line_wrapping = 7,
color320x200_graphics = 13,
color640x200_16color_graphics = 14,
mono640x350_2color_graphics = 15,
color640x350_16color_graphics = 16,
mono640x480_2color_graphics = 17,
color640x480_16color_graphics = 18,
color320_200_256color_graphics = 19
};
inline std::string use_mode(const mode mode)
{
return std::string(BLT_ANSI_CSI) + "=" + std::to_string(static_cast<u8>(mode)) + "h";
}
} }
#endif //BLT_LOGGING_COLORS_H #endif //BLT_LOGGING_COLORS_H

View File

@ -130,8 +130,14 @@ namespace blt::logging
default: default:
handle_type(stream, type); handle_type(stream, type);
if constexpr (blt::meta::is_streamable_v<T>) if constexpr (blt::meta::is_streamable_v<T>)
stream << t; {
else if constexpr (std::is_same_v<T, char> || std::is_same_v<T, signed char>)
stream << static_cast<int>(t);
else if constexpr (std::is_same_v<T, unsigned char>)
stream << static_cast<unsigned int>(t);
else
stream << t;
}else
stream << "{INVALID TYPE}"; stream << "{INVALID TYPE}";
} }
}; };

@ -1 +1 @@
Subproject commit 7ef2e733416953b222851f9a360d7fc72d068ee5 Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3

View File

@ -17,13 +17,13 @@
*/ */
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <blt/logging/ansi.h>
#include <blt/logging/logging.h> #include <blt/logging/logging.h>
#include <blt/std/assert.h> #include <blt/std/assert.h>
#include <blt/std/utility.h> #include <blt/std/utility.h>
struct some_silly_type_t struct some_silly_type_t
{ {};
};
auto expected_str = std::string(R"(This is a println! auto expected_str = std::string(R"(This is a println!
This is a println with args '42' This is a println with args '42'
@ -67,69 +67,91 @@ This is a println with arg reference &&&&&&&&&&&&&&&&&&&&
std::pair<bool, std::string> compare_strings(const std::string& s1, const std::string& s2) std::pair<bool, std::string> compare_strings(const std::string& s1, const std::string& s2)
{ {
const auto size = std::min(s1.size(), s2.size()); const auto size = std::min(s1.size(), s2.size());
size_t index = 0; size_t index = 0;
for (; index < size; ++index) for (; index < size; ++index)
{ {
if (s1[index] != s2[index]) if (s1[index] != s2[index])
{ {
std::stringstream ss; std::stringstream ss;
const auto i1 = std::max(static_cast<blt::i64>(index) - 32, 0l); const auto i1 = std::max(static_cast<blt::i64>(index) - 32, 0l);
const auto l1 = std::min(static_cast<blt::i64>(size) - i1, 65l); const auto l1 = std::min(static_cast<blt::i64>(size) - i1, 65l);
ss << "Strings differ at index " << index << "!\n"; ss << "Strings differ at index " << index << "!\n";
ss << "'" << s1.substr(i1, l1) << "' vs '" << s2.substr(i1, l1) << "'" << std::endl; ss << "'" << s1.substr(i1, l1) << "' vs '" << s2.substr(i1, l1) << "'" << std::endl;
return {false, ss.str()}; return {false, ss.str()};
} }
} }
if (s1.size() != s2.size()) if (s1.size() != s2.size())
return {false, "Strings size do not match '" + std::to_string(s1.size()) + "' vs '" + std::to_string(s2.size()) + "'"}; return {false, "Strings size do not match '" + std::to_string(s1.size()) + "' vs '" + std::to_string(s2.size()) + "'"};
return {true, ""}; return {true, ""};
} }
int main() int main()
{ {
std::stringstream ss; std::stringstream ss;
blt::logging::println(ss, "This is a println!"); blt::logging::println(ss, "This is a println!");
blt::logging::println(ss, "This is a println with args '{}'", 42); blt::logging::println(ss, "This is a println with args '{}'", 42);
blt::logging::println(ss, "This is a println with multiple args '{}' '{:.100}' '{}'", 42, 32.34231233f, "Hello World!"); blt::logging::println(ss, "This is a println with multiple args '{}' '{:.100}' '{}'", 42, 32.34231233f, "Hello World!");
blt::logging::println(ss, "This is a '{1}' fmt string with positionals '{0}'", "I am a string!", "Well so am I except cooler :3"); blt::logging::println(ss, "This is a '{1}' fmt string with positionals '{0}'", "I am a string!", "Well so am I except cooler :3");
blt::logging::println(ss, "This is a println with a sign {:+}", 4120); blt::logging::println(ss, "This is a println with a sign {:+}", 4120);
blt::logging::println(ss, "This is a println with a sign {:+}", -4120); blt::logging::println(ss, "This is a println with a sign {:+}", -4120);
blt::logging::println(ss, "This is a println with a space {: }", 4120); blt::logging::println(ss, "This is a println with a space {: }", 4120);
blt::logging::println(ss, "This is a println with a space {: }", -4120); blt::logging::println(ss, "This is a println with a space {: }", -4120);
blt::logging::println(ss, "This is a println with a minus {:-}", 4120); blt::logging::println(ss, "This is a println with a minus {:-}", 4120);
blt::logging::println(ss, "This is a println with a minus {:-}", -4120); blt::logging::println(ss, "This is a println with a minus {:-}", -4120);
blt::logging::println(ss, "This is a println with a with {:10}", 4120); blt::logging::println(ss, "This is a println with a with {:10}", 4120);
blt::logging::println(ss, "This is a println with a with leading zeros {:010}", 4120); blt::logging::println(ss, "This is a println with a with leading zeros {:010}", 4120);
blt::logging::println(ss, "This is a println with a precision {:.10f}", 42.232342349); blt::logging::println(ss, "This is a println with a precision {:.10f}", 42.232342349);
blt::logging::println(ss, "This is a println with hex {:.10x}", 4250); blt::logging::println(ss, "This is a println with hex {:.10x}", 4250);
blt::logging::println(ss, "This is a println with hex with leading {:#.10x}", 4250); blt::logging::println(ss, "This is a println with hex with leading {:#.10x}", 4250);
blt::logging::println(ss, "This is a println with binary {:#b}", 6969420); blt::logging::println(ss, "This is a println with binary {:#b}", 6969420);
blt::logging::println(ss, "This is a println with binary with space {: #b}", 6969421); blt::logging::println(ss, "This is a println with binary with space {: #b}", 6969421);
blt::logging::println(ss, "This is a println with binary with space {: b}", 69); blt::logging::println(ss, "This is a println with binary with space {: b}", 69);
blt::logging::println(ss, "This is a println with octal {:#o}", 6669); blt::logging::println(ss, "This is a println with octal {:#o}", 6669);
blt::logging::println(ss, "This is a println with hexfloat {:a}", 402.4320); blt::logging::println(ss, "This is a println with hexfloat {:a}", 402.4320);
blt::logging::println(ss, "This is a println with exponent {:e}", 44320902.4320); blt::logging::println(ss, "This is a println with exponent {:e}", 44320902.4320);
blt::logging::println(ss, "This is a println with exponent {:e}", 9532434234042340.0); blt::logging::println(ss, "This is a println with exponent {:e}", 9532434234042340.0);
blt::logging::println(ss, "This is a println with general {:g}", 953243.49); blt::logging::println(ss, "This is a println with general {:g}", 953243.49);
blt::logging::println(ss, "This is a println with general {:g}", 953243324023403240.49); blt::logging::println(ss, "This is a println with general {:g}", 953243324023403240.49);
blt::logging::println(ss, "This is a println with a char {:c}", 66); blt::logging::println(ss, "This is a println with a char {:c}", 66);
blt::logging::println(ss, "This is a println with type {:t}", some_silly_type_t{}); blt::logging::println(ss, "This is a println with type {:t}", some_silly_type_t{});
blt::logging::println(ss, "This is a println with boolean {}", true); blt::logging::println(ss, "This is a println with boolean {}", true);
blt::logging::println(ss, "This is a println with boolean as int {:d}", false); blt::logging::println(ss, "This is a println with boolean as int {:d}", false);
blt::logging::println(ss, "This is a println with boolean as hex {:#x}", true); blt::logging::println(ss, "This is a println with boolean as hex {:#x}", true);
blt::logging::println(ss, "This is a println with boolean as octal {:o}", true); blt::logging::println(ss, "This is a println with boolean as octal {:o}", true);
blt::logging::println(ss, "This is a println with alignment left {:<10} end value", 64); blt::logging::println(ss, "This is a println with alignment left {:<10} end value", 64);
blt::logging::println(ss, "This is a println with alignment right {:>10} end value", 46); blt::logging::println(ss, "This is a println with alignment right {:>10} end value", 46);
blt::logging::println(ss, "This is a println with alignment left (fill) {:*<10} end value", 46); blt::logging::println(ss, "This is a println with alignment left (fill) {:*<10} end value", 46);
blt::logging::println(ss, "This is a println with alignment right (fill) {:*>10} end value", 46); blt::logging::println(ss, "This is a println with alignment right (fill) {:*>10} end value", 46);
blt::logging::println(ss, "This is a println with alignment right (fill with reserved character) {:\\^>10} end value", 46); blt::logging::println(ss, "This is a println with alignment right (fill with reserved character) {:\\^>10} end value", 46);
blt::logging::println(ss, "This is a println with fill no alignment {:%20} end value", 46); blt::logging::println(ss, "This is a println with fill no alignment {:%20} end value", 46);
blt::logging::println(ss, "This is a println with arg reference {0:{1}.{2}f}", 46.0232, 20, 2); blt::logging::println(ss, "This is a println with arg reference {0:{1}.{2}f}", 46.0232, 20, 2);
blt::logging::println(ss, "This is a println with arg reference {0:&{1}}", "", 20); blt::logging::println(ss, "This is a println with arg reference {0:&{1}}", "", 20);
blt::logging::print(ss.str()); blt::logging::print(ss.str());
auto [passed, error_msg] = compare_strings(expected_str, ss.str()); auto [passed, error_msg] = compare_strings(expected_str, ss.str());
BLT_ASSERT_MSG(passed && "Logger logged string doesn't match precomputed expected string!", error_msg.c_str()); BLT_ASSERT_MSG(passed && "Logger logged string doesn't match precomputed expected string!", error_msg.c_str());
// blt::logging::println("This is println {}\twith a std::endl in the middle of it"); namespace ansi = blt::logging::ansi;
namespace color = ansi::color;
for (blt::u8 r = 0; r < 6; r++)
{
for (blt::u8 g = 0; g < 6; g++)
{
for (blt::u8 b = 0; b < 6; b++)
{
blt::logging::println("{}This is a println with a color {:#3x} {:#3x} {:#3x}{}",
build(fg(color::color256{r, g, b}), bg(color::color256{
static_cast<unsigned char>(5 - r),
static_cast<unsigned char>(5 - g),
static_cast<unsigned char>(5 - b)
})), r, g, b, build(color::color_mode::RESET_ALL));
}
}
}
blt::logging::println("{}This is a color now with background{}",
build(color::color_mode::BOLD, fg(color::color8::CYAN), color::color_mode::DIM, bg(color::color8::YELLOW)),
build(color::color_mode::RESET_ALL));
// blt::logging::println("This is println {}\twith a std::endl in the middle of it");
} }