forked from fmtlib/fmt
Add support for builtin terminal colors. (#974)
This commit is contained in:
committed by
Victor Zverovich
parent
61ad543c3e
commit
93fd473b8f
@ -191,6 +191,25 @@ enum class color : uint32_t {
|
|||||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||||
}; // enum class color
|
}; // 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 {
|
enum class emphasis : uint8_t {
|
||||||
bold = 1,
|
bold = 1,
|
||||||
italic = 1 << 1,
|
italic = 1 << 1,
|
||||||
@ -215,6 +234,32 @@ struct rgb {
|
|||||||
uint8_t b;
|
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.
|
// Experimental text formatting support.
|
||||||
class text_style {
|
class text_style {
|
||||||
public:
|
public:
|
||||||
@ -227,18 +272,18 @@ class text_style {
|
|||||||
set_foreground_color = rhs.set_foreground_color;
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
foreground_color = rhs.foreground_color;
|
foreground_color = rhs.foreground_color;
|
||||||
} else if (rhs.set_foreground_color) {
|
} else if (rhs.set_foreground_color) {
|
||||||
foreground_color.r |= rhs.foreground_color.r;
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
foreground_color.g |= rhs.foreground_color.g;
|
throw format_error("can't OR a terminal color");
|
||||||
foreground_color.b |= rhs.foreground_color.b;
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!set_background_color) {
|
if (!set_background_color) {
|
||||||
set_background_color = rhs.set_background_color;
|
set_background_color = rhs.set_background_color;
|
||||||
background_color = rhs.background_color;
|
background_color = rhs.background_color;
|
||||||
} else if (rhs.set_background_color) {
|
} else if (rhs.set_background_color) {
|
||||||
background_color.r |= rhs.background_color.r;
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
background_color.g |= rhs.background_color.g;
|
throw format_error("can't OR a terminal color");
|
||||||
background_color.b |= rhs.background_color.b;
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||||
@ -256,18 +301,18 @@ class text_style {
|
|||||||
set_foreground_color = rhs.set_foreground_color;
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
foreground_color = rhs.foreground_color;
|
foreground_color = rhs.foreground_color;
|
||||||
} else if (rhs.set_foreground_color) {
|
} else if (rhs.set_foreground_color) {
|
||||||
foreground_color.r &= rhs.foreground_color.r;
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
foreground_color.g &= rhs.foreground_color.g;
|
throw format_error("can't AND a terminal color");
|
||||||
foreground_color.b &= rhs.foreground_color.b;
|
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!set_background_color) {
|
if (!set_background_color) {
|
||||||
set_background_color = rhs.set_background_color;
|
set_background_color = rhs.set_background_color;
|
||||||
background_color = rhs.background_color;
|
background_color = rhs.background_color;
|
||||||
} else if (rhs.set_background_color) {
|
} else if (rhs.set_background_color) {
|
||||||
background_color.r &= rhs.background_color.r;
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
background_color.g &= rhs.background_color.g;
|
throw format_error("can't AND a terminal color");
|
||||||
background_color.b &= rhs.background_color.b;
|
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||||
@ -289,11 +334,11 @@ class text_style {
|
|||||||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||||
return static_cast<uint8_t>(ems) != 0;
|
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");
|
assert(has_foreground() && "no foreground specified for this style");
|
||||||
return foreground_color;
|
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");
|
assert(has_background() && "no background specified for this style");
|
||||||
return background_color;
|
return background_color;
|
||||||
}
|
}
|
||||||
@ -303,32 +348,37 @@ class text_style {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FMT_CONSTEXPR text_style(bool is_foreground, rgb text_color) FMT_NOEXCEPT
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
: set_foreground_color(), set_background_color(), ems() {
|
internal::color_type text_color) FMT_NOEXCEPT
|
||||||
if (is_foreground) {
|
: set_foreground_color(),
|
||||||
foreground_color = text_color;
|
set_background_color(),
|
||||||
set_foreground_color = true;
|
ems() {
|
||||||
} else {
|
if (is_foreground) {
|
||||||
background_color = text_color;
|
foreground_color = text_color;
|
||||||
set_background_color = true;
|
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 fg(internal::color_type foreground)
|
||||||
friend FMT_CONSTEXPR_DECL text_style bg(rgb background) FMT_NOEXCEPT;
|
FMT_NOEXCEPT;
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
rgb foreground_color;
|
internal::color_type foreground_color;
|
||||||
rgb background_color;
|
internal::color_type background_color;
|
||||||
bool set_foreground_color;
|
bool set_foreground_color;
|
||||||
bool set_background_color;
|
bool set_background_color;
|
||||||
emphasis ems;
|
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);
|
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);
|
return text_style(/*is_foreground=*/false, background);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,10 +390,37 @@ namespace internal {
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct ansi_color_escape {
|
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++) {
|
for (int i = 0; i < 7; i++) {
|
||||||
buffer[i] = static_cast<Char>(esc[i]);
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
}
|
}
|
||||||
|
rgb color(text_color.value.rgb_color);
|
||||||
to_esc(color.r, buffer + 7, ';');
|
to_esc(color.r, buffer + 7, ';');
|
||||||
to_esc(color.g, buffer + 11, ';');
|
to_esc(color.g, buffer + 11, ';');
|
||||||
to_esc(color.b, buffer + 15, 'm');
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
@ -388,14 +465,14 @@ private:
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char>
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
make_foreground_color(rgb color) FMT_NOEXCEPT {
|
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(color, internal::data::FOREGROUND_COLOR);
|
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char>
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
make_background_color(rgb color) FMT_NOEXCEPT {
|
make_background_color(internal::color_type background) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(color, internal::data::BACKGROUND_COLOR);
|
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
|
@ -233,4 +233,14 @@ TEST(ColorsTest, Colors) {
|
|||||||
EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"),
|
EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"),
|
||||||
"\x1b[38;2;000;000;255mblue log\x1b[0m");
|
"\x1b[38;2;000;000;255mblue log\x1b[0m");
|
||||||
EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), "hi"), "hi");
|
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");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user