From d7c1f9047a200164dd63921a88221d0c6cae70b5 Mon Sep 17 00:00:00 2001 From: Nicolas Lesser Date: Wed, 5 Dec 2018 17:38:40 +0100 Subject: [PATCH] Use a single class to denote text formatting. --- include/fmt/color.h | 264 +++++++++++++++++++++++--------------------- 1 file changed, 141 insertions(+), 123 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 67e07fdf..aa3ff451 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -47,7 +47,6 @@ inline void vprint_colored(color c, wstring_view format, wformat_args args) { #else -// Experimental color support. enum class color : uint32_t { alice_blue = 0xF0F8FF, // rgb(240,248,255) antique_white = 0xFAEBD7, // rgb(250,235,215) @@ -192,11 +191,11 @@ enum class color : uint32_t { yellow_green = 0x9ACD32 // rgb(154,205,50) }; // enum class color -enum class emphasis : uint8_t { - bold = 1, - italic = 3, - underline = 4, - strikethrough = 9 +enum class emphasis : uint32_t { + bold = 1 << 1, + italic = 1 << 3, + underline = 1 << 4, + strikethrough = 1 << 9 }; // enum class emphasis // rgb is a struct for red, green and blue colors. @@ -216,6 +215,108 @@ struct rgb { uint8_t b; }; +// Experimental text formatting support. +class text_format { +public: + FMT_CONSTEXPR_DECL text_format(emphasis em) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems(em) {} + + FMT_CONSTEXPR_DECL + text_format &operator|=(const text_format &rhs) FMT_NOEXCEPT { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + foreground_color.r |= rhs.foreground_color.r; + foreground_color.g |= rhs.foreground_color.g; + foreground_color.b |= rhs.foreground_color.b; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + background_color.r |= rhs.background_color.r; + background_color.g |= rhs.background_color.g; + background_color.b |= rhs.background_color.b; + } + + ems = (emphasis)((uint32_t)ems | (uint32_t)rhs.ems); + return *this; + } + + friend FMT_CONSTEXPR_DECL + text_format operator|(text_format lhs, const text_format &rhs) FMT_NOEXCEPT { + return lhs |= rhs; + } + + FMT_CONSTEXPR_DECL + text_format &operator&=(const text_format &rhs) FMT_NOEXCEPT { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + foreground_color.r &= rhs.foreground_color.r; + foreground_color.g &= rhs.foreground_color.g; + foreground_color.b &= rhs.foreground_color.b; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + background_color.r &= rhs.background_color.r; + background_color.g &= rhs.background_color.g; + background_color.b &= rhs.background_color.b; + } + + ems = (emphasis)((uint32_t)ems & (uint32_t)rhs.ems); + return *this; + } + + friend FMT_CONSTEXPR_DECL + text_format operator&(text_format lhs, const text_format &rhs) FMT_NOEXCEPT { + return lhs &= rhs; + } + + private: + FMT_CONSTEXPR_DECL text_format(bool is_foreground, + rgb text_color) FMT_NOEXCEPT + : set_foreground_color(), set_background_color(), ems() { + if (is_foreground) { + foreground_color = text_color; + set_foreground_color = true; + } + else { + background_color = text_color; + set_background_color = true; + } + } + + friend FMT_CONSTEXPR_DECL text_format fg(rgb foreground) FMT_NOEXCEPT; + friend FMT_CONSTEXPR_DECL text_format bg(rgb background) FMT_NOEXCEPT; + template + friend void vprint_text_format( + const text_format &tf, const S &format, + basic_format_args::type> args); + + rgb foreground_color; + rgb background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +FMT_CONSTEXPR_DECL text_format fg(rgb foreground) FMT_NOEXCEPT { + return text_format(/*is_foreground=*/true, foreground); +} + +FMT_CONSTEXPR_DECL text_format bg(rgb background) FMT_NOEXCEPT { + return text_format(/*is_foreground=*/false, background); +} + namespace internal { template @@ -230,11 +331,26 @@ struct ansi_color_escape { buffer[19] = static_cast(0); } FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { - buffer[0] = '\x1b'; - buffer[1] = '['; - buffer[2] = '0' + (uint8_t)em; - buffer[3] = 'm'; - buffer[4] = '\0'; + uint8_t em_codes[4] = {}; + if ((uint32_t)em & (uint32_t)emphasis::bold) + em_codes[0] = 1; + if ((uint32_t)em & (uint32_t)emphasis::underline) + em_codes[1] = 3; + if ((uint32_t)em & (uint32_t)emphasis::italic) + em_codes[2] = 4; + if ((uint32_t)em & (uint32_t)emphasis::strikethrough) + em_codes[3] = 9; + + std::size_t index = 0; + for (int i = 0; i < 4; ++i) { + if (!em_codes[i]) + continue; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + buffer[index++] = static_cast('0' + em_codes[i]); + buffer[index++] = static_cast('m'); + } + buffer[index++] = static_cast(0); } FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; } @@ -262,7 +378,8 @@ make_background_color(rgb color) FMT_NOEXCEPT { } template -FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { +FMT_CONSTEXPR ansi_color_escape +make_emphasis(emphasis em) FMT_NOEXCEPT { return ansi_color_escape(em); } @@ -289,134 +406,35 @@ inline void reset_color(FILE *stream) FMT_NOEXCEPT { template < typename S, typename Char = typename internal::char_t::type> -void vprint_rgb(rgb fd, const S &format, +void vprint_text_format(const text_format &tf, const S &format, basic_format_args::type> args) { - internal::fputs(internal::make_foreground_color(fd), stdout); - vprint(format, args); - internal::reset_color(stdout); -} + internal::fputs(internal::make_emphasis(tf.ems), stdout); -template < - typename S, typename Char = typename internal::char_t::type> -void vprint_rgb(rgb fd, rgb bg, const S &format, - basic_format_args::type> args) { - internal::fputs(internal::make_foreground_color(fd), stdout); - internal::fputs(internal::make_background_color(bg), stdout); - vprint(format, args); - internal::reset_color(stdout); -} - -template ::type> -void vprint_emphasis( - emphasis em, const S &format, - basic_format_args::type> args) { - internal::fputs(internal::make_emphasis(em), stdout); - vprint(format, args); - internal::reset_color(stdout); -} - -template ::type> -void vprint_emphasis( - emphasis em, rgb fd, const S &format, - basic_format_args::type> args) { - internal::fputs(internal::make_emphasis(em), stdout); - internal::fputs(internal::make_foreground_color(fd), stdout); - vprint(format, args); - internal::reset_color(stdout); -} - -template ::type> -void vprint_emphasis( - emphasis em, rgb fd, rgb bg, const S &format, - basic_format_args::type> args) { - internal::fputs(internal::make_emphasis(em), stdout); - internal::fputs(internal::make_foreground_color(fd), stdout); - internal::fputs(internal::make_background_color(bg), stdout); + if (tf.set_foreground_color) + internal::fputs( + internal::make_foreground_color(tf.foreground_color), stdout); + if (tf.set_background_color) + internal::fputs( + internal::make_background_color(tf.background_color), stdout); vprint(format, args); internal::reset_color(stdout); } /** Formats a string and prints it to stdout using ANSI escape sequences to - specify foreground color 'fd'. + specify text formatting. Example: - fmt::print(fmt::color::red, "Elapsed time: {0:.2f} seconds", 1.23); - */ -template -typename std::enable_if::value>::type -print(rgb fd, const String &format_str, const Args & ... args) { - internal::check_format_string(format_str); - typedef typename internal::char_t::type char_t; - typedef typename buffer_context::type context_t; - format_arg_store as{args...}; - vprint_rgb(fd, format_str, basic_format_args(as)); -} - -/** - Formats a string and prints it to stdout using ANSI escape sequences to - specify foreground color 'fd' and background color 'bg'. - Example: - fmt::print(fmt::color::red, fmt::color::black, + fmt::print(fmt::emphasis::bold | fmt::color::red, "Elapsed time: {0:.2f} seconds", 1.23); */ template typename std::enable_if::value>::type -print(rgb fd, rgb bg, const String &format_str, const Args & ... args) { +print(const text_format &tf, const String &format_str, const Args & ... args) { internal::check_format_string(format_str); typedef typename internal::char_t::type char_t; typedef typename buffer_context::type context_t; format_arg_store as{args...}; - vprint_rgb(fd, bg, format_str, basic_format_args(as)); -} - -/** - Formats a string and prints it to stdout using ANSI escape sequences to - specify the text emphasis 'em'. - Example: - fmt::print(fmt::emphasis::bold, "Be careful!"); - */ -template -typename std::enable_if::value>::type print( - emphasis em, const String &format_str, const Args &... args) { - internal::check_format_string(format_str); - typedef typename internal::char_t::type char_t; - typedef typename buffer_context::type context_t; - format_arg_store as{args...}; - vprint_emphasis(em, format_str, basic_format_args(as)); -} - -/** - Formats a string and prints it to stdout using ANSI escape sequences to - specify the text emphasis 'em' and the foreground color 'fd'. - Example: - fmt::print(fmt::emphasis::bold, fmt::color::red, "Be careful!"); - */ -template -typename std::enable_if::value>::type print( - emphasis em, rgb fd, const String &format_str, const Args &... args) { - internal::check_format_string(format_str); - typedef typename internal::char_t::type char_t; - typedef typename buffer_context::type context_t; - format_arg_store as{args...}; - vprint_emphasis(em, fd, format_str, basic_format_args(as)); -} - -/** - Formats a string and prints it to stdout using ANSI escape sequences to - specify the text emphasis 'em', the foreground color 'fg' and the background - color 'bg'. - Example: - fmt::print(fmt::emphasis::bold, "Be careful!"); - */ -template -typename std::enable_if::value>::type print( - emphasis em, rgb fd, rgb bg, const String &format_str, - const Args &... args) { - internal::check_format_string(format_str); - typedef typename internal::char_t::type char_t; - typedef typename buffer_context::type context_t; - format_arg_store as{args...}; - vprint_emphasis(em, fd, bg, format_str, basic_format_args(as)); + vprint_text_format(tf, format_str, basic_format_args(as)); } #endif