Add experimental emphasis support (#961)

This commit is contained in:
Nicolas 2018-12-06 16:56:01 +01:00 committed by Victor Zverovich
parent 7c4eb0fbeb
commit 81b5c4a5fd
2 changed files with 193 additions and 37 deletions

View File

@ -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<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(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<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(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<uint8_t>(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 <typename Char>
@ -222,6 +354,29 @@ struct ansi_color_escape {
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold))
em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::italic))
em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline))
em_codes[2] = 4;
if (em_bits & static_cast<uint8_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<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(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<Char>(color, internal::data::BACKGROUND_COLOR);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
@ -270,54 +431,35 @@ inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
template <
typename S, typename Char = typename internal::char_t<S>::type>
void vprint_rgb(rgb fd, const S &format,
void vprint(const text_style &tf, const S &format,
basic_format_args<typename buffer_context<Char>::type> args) {
internal::fputs<Char>(internal::make_foreground_color<Char>(fd), stdout);
vprint(format, args);
internal::reset_color<Char>(stdout);
}
template <
typename S, typename Char = typename internal::char_t<S>::type>
void vprint_rgb(rgb fd, rgb bg, const S &format,
basic_format_args<typename buffer_context<Char>::type> args) {
internal::fputs<Char>(internal::make_foreground_color<Char>(fd), stdout);
internal::fputs<Char>(internal::make_background_color<Char>(bg), stdout);
if (tf.has_emphasis())
internal::fputs<Char>(internal::make_emphasis<Char>(tf.get_emphasis()), stdout);
if (tf.has_foreground())
internal::fputs<Char>(
internal::make_foreground_color<Char>(tf.get_foreground()), stdout);
if (tf.has_background())
internal::fputs<Char>(
internal::make_background_color<Char>(tf.get_background()), stdout);
vprint(format, args);
internal::reset_color<Char>(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 String, typename... Args>
typename std::enable_if<internal::is_string<String>::value>::type
print(rgb fd, const String &format_str, const Args & ... args) {
internal::check_format_string<Args...>(format_str);
typedef typename internal::char_t<String>::type char_t;
typedef typename buffer_context<char_t>::type context_t;
format_arg_store<context_t, Args...> as{args...};
vprint_rgb(fd, format_str, basic_format_args<context_t>(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 | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename String, typename... Args>
typename std::enable_if<internal::is_string<String>::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<Args...>(format_str);
typedef typename internal::char_t<String>::type char_t;
typedef typename buffer_context<char_t>::type context_t;
format_arg_store<context_t, Args...> as{args...};
vprint_rgb(fd, bg, format_str, basic_format_args<context_t>(as));
vprint(tf, format_str, basic_format_args<context_t>(as));
}
#endif

View File

@ -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");
}