Add support for builtin terminal colors. (#974)
diff --git a/include/fmt/color.h b/include/fmt/color.h
index 78b7895..3898587 100644
--- a/include/fmt/color.h
+++ b/include/fmt/color.h
@@ -191,6 +191,25 @@
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
+enum class terminal_color : uint8_t {
+ black = 30,
+ red,
+ green,
+ yellow,
+ blue,
+ magenta,
+ cyan,
+ white,
+ bright_black = 90,
+ bright_red,
+ bright_green,
+ bright_yellow,
+ bright_blue,
+ bright_magenta,
+ bright_cyan,
+ bright_white
+}; // enum class terminal_color
+
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
@@ -215,6 +234,32 @@
uint8_t b;
};
+namespace internal {
+
+// color is a struct of either a rgb color or a terminal color.
+struct color_type {
+ FMT_CONSTEXPR color_type() FMT_NOEXCEPT
+ : is_rgb(), value{} {}
+ FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
+ : is_rgb(true), value{} {
+ value.rgb_color = static_cast<uint32_t>(rgb_color);
+ }
+ FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
+ : is_rgb(true), value{} {
+ value.rgb_color = (rgb_color.r << 16) + (rgb_color.g << 8) + rgb_color.b;
+ }
+ FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
+ : is_rgb(), value{} {
+ value.term_color = static_cast<uint8_t>(term_color);
+ }
+ bool is_rgb;
+ union color_union {
+ uint8_t term_color;
+ uint32_t rgb_color;
+ } value;
+};
+} // namespace internal
+
// Experimental text formatting support.
class text_style {
public:
@@ -227,18 +272,18 @@
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 (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
+ throw format_error("can't OR a terminal color");
+ foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
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;
+ if (!background_color.is_rgb || !rhs.background_color.is_rgb)
+ throw format_error("can't OR a terminal color");
+ background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
@@ -256,18 +301,18 @@
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 (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
+ throw format_error("can't AND a terminal color");
+ foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
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;
+ if (!background_color.is_rgb || !rhs.background_color.is_rgb)
+ throw format_error("can't AND a terminal color");
+ background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
@@ -289,11 +334,11 @@
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
- FMT_CONSTEXPR rgb get_foreground() const FMT_NOEXCEPT {
+ FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
return foreground_color;
}
- FMT_CONSTEXPR rgb get_background() const FMT_NOEXCEPT {
+ FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
return background_color;
}
@@ -303,32 +348,37 @@
}
private:
- FMT_CONSTEXPR 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;
- }
- }
+ FMT_CONSTEXPR text_style(bool is_foreground,
+ internal::color_type 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;
+ friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
+ FMT_NOEXCEPT;
+ friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
+ FMT_NOEXCEPT;
- rgb foreground_color;
- rgb background_color;
+ internal::color_type foreground_color;
+ internal::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
-FMT_CONSTEXPR text_style fg(rgb foreground) FMT_NOEXCEPT {
+FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
-FMT_CONSTEXPR text_style bg(rgb background) FMT_NOEXCEPT {
+FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
@@ -340,10 +390,37 @@
template <typename Char>
struct ansi_color_escape {
- FMT_CONSTEXPR ansi_color_escape(rgb color, const char * esc) FMT_NOEXCEPT {
+ FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, const char * esc) FMT_NOEXCEPT {
+ // If we have a terminal color, we need to output another escape code
+ // sequence.
+ if (!text_color.is_rgb) {
+ bool is_background = esc == internal::data::BACKGROUND_COLOR;
+ uint8_t value = text_color.value.term_color;
+ // Background ASCII codes are the same as the foreground ones but with
+ // 10 more.
+ if (is_background)
+ value += 10;
+
+ std::size_t index = 0;
+ buffer[index++] = static_cast<Char>('\x1b');
+ buffer[index++] = static_cast<Char>('[');
+
+ if (value >= 100) {
+ buffer[index++] = static_cast<Char>('1');
+ value %= 100;
+ }
+ buffer[index++] = static_cast<Char>('0' + value / 10);
+ buffer[index++] = static_cast<Char>('0' + value % 10);
+
+ buffer[index++] = static_cast<Char>('m');
+ buffer[index++] = static_cast<Char>('\0');
+ return;
+ }
+
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
+ rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
@@ -388,14 +465,14 @@
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
-make_foreground_color(rgb color) FMT_NOEXCEPT {
- return ansi_color_escape<Char>(color, internal::data::FOREGROUND_COLOR);
+make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
+ return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
-make_background_color(rgb color) FMT_NOEXCEPT {
- return ansi_color_escape<Char>(color, internal::data::BACKGROUND_COLOR);
+make_background_color(internal::color_type background) FMT_NOEXCEPT {
+ return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
}
template <typename Char>
diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc
index f4d75aa..8d20e77 100644
--- a/test/format-impl-test.cc
+++ b/test/format-impl-test.cc
@@ -233,4 +233,14 @@
EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"),
"\x1b[38;2;000;000;255mblue log\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), "hi"), "hi");
+ EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), "tred"),
+ "\x1b[31mtred\x1b[0m");
+ EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::cyan), "tcyan"),
+ "\x1b[46mtcyan\x1b[0m");
+ EXPECT_WRITE(stdout,
+ fmt::print(fg(fmt::terminal_color::bright_green), "tbgreen"),
+ "\x1b[92mtbgreen\x1b[0m");
+ EXPECT_WRITE(stdout,
+ fmt::print(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
+ "\x1b[105mtbmagenta\x1b[0m");
}