From 08e19c067e57c92c67a98946a700b680cef95e06 Mon Sep 17 00:00:00 2001 From: Brett Date: Wed, 5 Mar 2025 01:55:14 -0500 Subject: [PATCH] ansi --- CMakeLists.txt | 2 +- include/blt/logging/ansi.h | 420 +++++++++++++++++++++++----------- include/blt/logging/logging.h | 10 +- libraries/parallel-hashmap | 2 +- tests/logger_tests.cpp | 146 +++++++----- 5 files changed, 382 insertions(+), 198 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6480ed1..b0e6bde 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) include(cmake/color.cmake) -set(BLT_VERSION 5.1.10) +set(BLT_VERSION 5.1.11) set(BLT_TARGET BLT) diff --git a/include/blt/logging/ansi.h b/include/blt/logging/ansi.h index 1aa7c83..1113332 100644 --- a/include/blt/logging/ansi.h +++ b/include/blt/logging/ansi.h @@ -19,7 +19,10 @@ #ifndef BLT_LOGGING_COLORS_H #define BLT_LOGGING_COLORS_H +#include +#include #include +#include #include #define BLT_ANSI_ESCAPE "\x1B" @@ -30,155 +33,308 @@ // https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797 namespace blt::logging::ansi { - namespace color - { - enum class color_mode : i32 - { - RESET_ALL = 0, - BOLD = 1, - DIM = 2, - ITALIC = 3, - UNDERLINE = 4, - BLINK = 5, - REVERSE = 7, - HIDDEN = 8, - STRIKE_THROUGH = 9, - }; + namespace color + { + inline std::array reset_sequences = {0, 22, 22, 23, 24, 25, 26, 27, 28, 29}; - enum class color8 : i32 - { - BLACK = 0, - RED = 1, - GREEN = 2, - YELLOW = 3, - BLUE = 4, - MAGENTA = 5, - CYAN = 6, - WHITE = 7, - DEFAULT = 9 - }; + enum class color_mode : u8 + { + RESET_ALL = 0, + BOLD = 1, + DIM = 2, + ITALIC = 3, + UNDERLINE = 4, + BLINK = 5, + REVERSE = 7, + HIDDEN = 8, + STRIKE_THROUGH = 9, }; - enum class color8_bright : i32 - { - BLACK = 0, - RED = 1, - GREEN = 2, - YELLOW = 3, - BLUE = 4, - MAGENTA = 5, - CYAN = 6, - WHITE = 7, - }; + enum class color8 : u8 + { + BLACK = 0, + RED = 1, + GREEN = 2, + YELLOW = 3, + BLUE = 4, + MAGENTA = 5, + CYAN = 6, + WHITE = 7, + DEFAULT = 9 + }; - namespace detail - { - template - struct color_converter - { - }; + enum class color8_bright : u8 + { + BLACK = 0, + RED = 1, + GREEN = 2, + YELLOW = 3, + BLUE = 4, + MAGENTA = 5, + CYAN = 6, + WHITE = 7, }; - template <> - struct color_converter - { - static std::string to_string(color8 color, const bool background) - { - return (background ? "4" : "3") + std::to_string(static_cast(color)); - } - }; + struct rgb_t + { + u8 r, g, b; + }; - template <> - struct color_converter - { - static std::string to_string(color8_bright color, const bool background) - { - return (background ? "10" : "9") + std::to_string(static_cast(color)); - } - }; - } - } + struct color256 + { + explicit color256(u8 index) : color(index) + {} - 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 = "\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; - } + color256(const u8 r, const u8 g, const u8 b) : color(rgb_t{r, g, b}) + { + if (r > 5) + throw std::invalid_argument("r must be between 0 and 5"); + if (g > 5) + throw std::invalid_argument("g must be between 0 and 5"); + if (b > 5) + throw std::invalid_argument("b must be between 0 and 5"); + } - namespace cursor - { - inline const std::string home = BLT_ANSI_CSI "H"; + [[nodiscard]] u8 index() const + { + if (std::holds_alternative(color)) + return std::get(color); + const auto [r, g, b] = std::get(color); + return (r * 36) + (g * 6) + b + 16; + } - template - inline std::string move_to(i64 line, 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"; - } + private: + std::variant color; + }; - inline std::string move_up(i64 lines) - { - return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "A"; - } + struct color_rgb + { + color_rgb(const u8 r, const u8 g, const u8 b) : color(rgb_t{r, g, b}) + {} - inline std::string move_down(i64 lines) - { - return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "B"; - } + rgb_t color; + }; - inline std::string move_right(i64 columns) - { - return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "C"; - } + namespace detail + { + template + struct color_holder + { + using value_type = T; - inline std::string move_left(i64 columns) - { - return std::string(BLT_ANSI_CSI) + std::to_string(columns) + "D"; - } + T color; + bool alt = false; + }; - inline std::string move_begin_down(i64 lines) - { - return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "E"; - } + template + struct color_converter + {}; - inline std::string move_begin_up(i64 lines) - { - return std::string(BLT_ANSI_CSI) + std::to_string(lines) + "F"; - } + template <> + struct color_converter + { + static std::string to_string(const color_holder color) + { + return (color.alt ? "4" : "3") + std::to_string(static_cast(color.color)); + } + }; - inline std::string move_to(i64 column) - { - return std::string(BLT_ANSI_CSI) + std::to_string(column) + "G"; - } + template <> + struct color_converter + { + static std::string to_string(const color_holder color) + { + return (color.alt ? "10" : "9") + std::to_string(static_cast(color.color)); + } + }; - 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 resotre_cursor_position_dec = BLT_ANSI_ESCAPE " 8"; - inline const std::string save_cursor_position_sco = BLT_ANSI_CSI "s"; - inline const std::string resotre_cursor_position_sco = BLT_ANSI_CSI "u"; - } + template <> + struct color_converter + { + static std::string to_string(const color_holder color) + { + return color.alt ? std::to_string(reset_sequences[static_cast(color.color)]) : std::to_string(static_cast(color.color)); + } + }; - 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"; - } + template <> + struct color_converter + { + static std::string to_string(const color_holder color) + { + return (color.alt ? "48;5;" : "38;5;") + std::to_string(color.color.index()); + } + }; + + template <> + struct color_converter + { + static std::string to_string(const color_holder 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 + struct ensure_holder + { + static color_holder make(const T& color) + { + return color_holder{color, false}; + } + }; + + template + struct ensure_holder> + { + static color_holder make(const color_holder& color) + { + return color; + } + }; + + template + struct decay + { + using type = std::decay_t; + }; + + template + struct decay> + { + using type = std::decay_t; + }; + + template + using decay_t = typename decay::type; + } + + template + auto fg(const T& color) + { + return detail::color_holder{color, false}; + } + + template + auto bg(const T& color) + { + return detail::color_holder{color, true}; + } + + template + std::string build(const Args&... args) + { + std::string result = BLT_ANSI_CSI; + ((result += detail::color_converter>::to_string(detail::ensure_holder::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 + 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(mode)) + "h"; + } } #endif //BLT_LOGGING_COLORS_H diff --git a/include/blt/logging/logging.h b/include/blt/logging/logging.h index f2d1b3c..f8789d4 100644 --- a/include/blt/logging/logging.h +++ b/include/blt/logging/logging.h @@ -130,8 +130,14 @@ namespace blt::logging default: handle_type(stream, type); if constexpr (blt::meta::is_streamable_v) - stream << t; - else + { + if constexpr (std::is_same_v || std::is_same_v) + stream << static_cast(t); + else if constexpr (std::is_same_v) + stream << static_cast(t); + else + stream << t; + }else stream << "{INVALID TYPE}"; } }; diff --git a/libraries/parallel-hashmap b/libraries/parallel-hashmap index 7ef2e73..93201da 160000 --- a/libraries/parallel-hashmap +++ b/libraries/parallel-hashmap @@ -1 +1 @@ -Subproject commit 7ef2e733416953b222851f9a360d7fc72d068ee5 +Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3 diff --git a/tests/logger_tests.cpp b/tests/logger_tests.cpp index bf815c5..0f318cd 100644 --- a/tests/logger_tests.cpp +++ b/tests/logger_tests.cpp @@ -17,13 +17,13 @@ */ #include #include +#include #include #include #include struct some_silly_type_t -{ -}; +{}; auto expected_str = std::string(R"(This is a println! This is a println with args '42' @@ -67,69 +67,91 @@ This is a println with arg reference &&&&&&&&&&&&&&&&&&&& std::pair compare_strings(const std::string& s1, const std::string& s2) { - const auto size = std::min(s1.size(), s2.size()); - size_t index = 0; - for (; index < size; ++index) - { - if (s1[index] != s2[index]) - { - std::stringstream ss; - const auto i1 = std::max(static_cast(index) - 32, 0l); - const auto l1 = std::min(static_cast(size) - i1, 65l); - ss << "Strings differ at index " << index << "!\n"; - ss << "'" << s1.substr(i1, l1) << "' vs '" << s2.substr(i1, l1) << "'" << std::endl; - return {false, ss.str()}; - } - } - if (s1.size() != s2.size()) - return {false, "Strings size do not match '" + std::to_string(s1.size()) + "' vs '" + std::to_string(s2.size()) + "'"}; - return {true, ""}; + const auto size = std::min(s1.size(), s2.size()); + size_t index = 0; + for (; index < size; ++index) + { + if (s1[index] != s2[index]) + { + std::stringstream ss; + const auto i1 = std::max(static_cast(index) - 32, 0l); + const auto l1 = std::min(static_cast(size) - i1, 65l); + ss << "Strings differ at index " << index << "!\n"; + ss << "'" << s1.substr(i1, l1) << "' vs '" << s2.substr(i1, l1) << "'" << std::endl; + return {false, ss.str()}; + } + } + if (s1.size() != s2.size()) + return {false, "Strings size do not match '" + std::to_string(s1.size()) + "' vs '" + std::to_string(s2.size()) + "'"}; + return {true, ""}; } int main() { - std::stringstream ss; - 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 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 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 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 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 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 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}", 69); - 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 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 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 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 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 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 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 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 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 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::print(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()); + std::stringstream ss; + 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 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 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 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 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 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 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}", 69); + 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 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 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 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 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 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 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 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 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 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::print(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::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(5 - r), + static_cast(5 - g), + static_cast(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"); }