From 81b5c4a5fdb7a8d61ed208646393d88bb8d1922b Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 6 Dec 2018 16:56:01 +0100 Subject: [PATCH] Add experimental emphasis support (#961) --- include/fmt/color.h | 208 ++++++++++++++++++++++++++++++++------- test/format-impl-test.cc | 22 ++++- 2 files changed, 193 insertions(+), 37 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 4d7193ce8d8e..4dbb37d339d0 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,6 +191,13 @@ enum class color : uint32_t { yellow_green = 0x9ACD32 // rgb(154,205,50) }; // enum class color +enum class emphasis : uint8_t { + bold = 1, + italic = 1 << 1, + underline = 1 << 2, + strikethrough = 1 << 3 +}; // enum class emphasis + // rgb is a struct for red, green and blue colors. // We use rgb as name because some editors will show it as color direct in the // editor. @@ -209,6 +215,132 @@ struct rgb { uint8_t b; }; +// Experimental text formatting support. +class text_style { +public: + FMT_CONSTEXPR_DECL text_style(emphasis em) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems(em) {} + + FMT_CONSTEXPR_DECL + text_style &operator|=(const text_style &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 = static_cast(static_cast(ems) | + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR_DECL + text_style operator|(text_style lhs, const text_style &rhs) FMT_NOEXCEPT { + return lhs |= rhs; + } + + FMT_CONSTEXPR_DECL + text_style &operator&=(const text_style &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 = static_cast(static_cast(ems) & + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR_DECL + text_style operator&(text_style lhs, const text_style &rhs) FMT_NOEXCEPT { + return lhs &= rhs; + } + + FMT_CONSTEXPR_DECL bool has_foreground() const FMT_NOEXCEPT { + return set_foreground_color; + } + FMT_CONSTEXPR_DECL bool has_background() const FMT_NOEXCEPT { + return set_background_color; + } + FMT_CONSTEXPR_DECL bool has_emphasis() const FMT_NOEXCEPT { + return static_cast(ems) != 0; + } + FMT_CONSTEXPR_DECL rgb get_foreground() const FMT_NOEXCEPT { + assert(has_foreground() && "no foreground specified for this style"); + return foreground_color; + } + FMT_CONSTEXPR_DECL rgb get_background() const FMT_NOEXCEPT { + assert(has_background() && "no background specified for this style"); + return background_color; + } + FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { + assert(has_emphasis() && "no emphasis specified for this style"); + return ems; + } + +private: + FMT_CONSTEXPR_DECL text_style(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_style fg(rgb foreground) FMT_NOEXCEPT; + friend FMT_CONSTEXPR_DECL text_style bg(rgb background) FMT_NOEXCEPT; + + rgb foreground_color; + rgb background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +FMT_CONSTEXPR_DECL text_style fg(rgb foreground) FMT_NOEXCEPT { + return text_style(/*is_foreground=*/true, foreground); +} + +FMT_CONSTEXPR_DECL text_style bg(rgb background) FMT_NOEXCEPT { + return text_style(/*is_foreground=*/false, background); +} + +FMT_CONSTEXPR_DECL text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { + return text_style(lhs) | rhs; +} + namespace internal { template @@ -222,6 +354,29 @@ struct ansi_color_escape { to_esc(color.b, buffer + 15, 'm'); buffer[19] = static_cast(0); } + FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { + uint8_t em_codes[4] = {}; + uint8_t em_bits = static_cast(em); + if (em_bits & static_cast(emphasis::bold)) + em_codes[0] = 1; + if (em_bits & static_cast(emphasis::italic)) + em_codes[1] = 3; + if (em_bits & static_cast(emphasis::underline)) + em_codes[2] = 4; + if (em_bits & static_cast(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; } private: @@ -247,6 +402,12 @@ make_background_color(rgb color) FMT_NOEXCEPT { return ansi_color_escape(color, internal::data::BACKGROUND_COLOR); } +template +FMT_CONSTEXPR ansi_color_escape +make_emphasis(emphasis em) FMT_NOEXCEPT { + return ansi_color_escape(em); +} + template inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT { std::fputs(chars, stream); @@ -270,54 +431,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, - basic_format_args::type> args) { - internal::fputs(internal::make_foreground_color(fd), stdout); - vprint(format, args); - internal::reset_color(stdout); -} - -template < - typename S, typename Char = typename internal::char_t::type> -void vprint_rgb(rgb fd, rgb bg, const S &format, +void vprint(const text_style &tf, const S &format, basic_format_args::type> args) { - internal::fputs(internal::make_foreground_color(fd), stdout); - internal::fputs(internal::make_background_color(bg), stdout); + if (tf.has_emphasis()) + internal::fputs(internal::make_emphasis(tf.get_emphasis()), stdout); + if (tf.has_foreground()) + internal::fputs( + internal::make_foreground_color(tf.get_foreground()), stdout); + if (tf.has_background()) + internal::fputs( + internal::make_background_color(tf.get_background()), 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'. - 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'. + specify text formatting. Example: - fmt::print(fmt::color::red, fmt::color::black, + fmt::print(fmt::emphasis::bold | fg(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_style &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)); + vprint(tf, format_str, basic_format_args(as)); } #endif diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index e06dd4445354..c40fe3b51155 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -207,11 +207,25 @@ TEST(FormatTest, CountCodePoints) { } TEST(ColorsTest, Colors) { - EXPECT_WRITE(stdout, fmt::print(fmt::rgb(255,20,30), "rgb(255,20,30)"), + EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); - EXPECT_WRITE(stdout, fmt::print(fmt::color::blue, "blue"), + EXPECT_WRITE(stdout, fmt::print(fg(fmt::color::blue), "blue"), "\x1b[38;2;000;000;255mblue\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), "two color"), + "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::bold, "bold"), + "\x1b[1mbold\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::italic, "italic"), + "\x1b[3mitalic\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::underline, "underline"), + "\x1b[4munderline\x1b[0m"); EXPECT_WRITE(stdout, - fmt::print(fmt::color::blue, fmt::color::red, "two color"), - "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); + fmt::print(fmt::emphasis::strikethrough, "strikethrough"), + "\x1b[9mstrikethrough\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); }