From ae1de3a8d3a2047e7a6d71f742dfb9771961e40b Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Sun, 30 Dec 2018 16:49:34 +0100 Subject: [PATCH] Add support for using text_style in format and vformat directly (#993) * Closes #993 --- include/fmt/color.h | 74 ++++++++++++++++++++++++++++++++++++++++ test/format-impl-test.cc | 34 +++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 5db861c9..f38455a4 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -452,6 +452,13 @@ struct ansi_color_escape { } FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; } + FMT_CONSTEXPR const Char * begin() const FMT_NOEXCEPT { + return buffer; + } + FMT_CONSTEXPR const Char * end() const FMT_NOEXCEPT { + return buffer + std::strlen(buffer); + } + private: Char buffer[7u + 3u * 4u + 1u]; @@ -502,6 +509,13 @@ inline void reset_color(FILE *stream) FMT_NOEXCEPT { fputs(internal::data::WRESET_COLOR, stream); } +template +inline void reset_color(basic_memory_buffer& buffer) FMT_NOEXCEPT { + const char* begin = data::RESET_COLOR; + const char* end = begin + sizeof(data::RESET_COLOR) - 1; + buffer.append(begin, end); +} + // The following specialiazation disables using std::FILE as a character type, // which is needed because or else // fmt::print(stderr, fmt::emphasis::bold, ""); @@ -510,6 +524,37 @@ template <> struct is_string : std::false_type {}; template <> struct is_string : std::false_type {}; + +template +std::basic_string vformat( + const text_style &ts, basic_string_view format_str, + basic_format_args::type> args) { + basic_memory_buffer buffer; + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + ansi_color_escape escape = + make_emphasis(ts.get_emphasis()); + buffer.append(escape.begin(), escape.end()); + } + if (ts.has_foreground()) { + has_style = true; + ansi_color_escape escape = + make_foreground_color(ts.get_foreground()); + buffer.append(escape.begin(), escape.end()); + } + if (ts.has_background()) { + has_style = true; + ansi_color_escape escape = + make_background_color(ts.get_background()); + buffer.append(escape.begin(), escape.end()); + } + internal::vformat_to(buffer, format_str, args); + if (has_style) { + reset_color(buffer); + } + return fmt::to_string(buffer); +} } // namespace internal template < @@ -570,6 +615,35 @@ typename std::enable_if::value>::type print( return print(stdout, ts, format_str, args...); } + +template +inline std::basic_string vformat( + const text_style &ts, + const S &format_str, + basic_format_args::type> args) { + return internal::vformat(ts, to_string_view(format_str), args); +} + +/** + \rst + Formats arguments and returns the result as a string using ANSI + escape sequences to specify text formatting. + + **Example**:: + + #include + std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}", 42); + \endrst +*/ +template +inline std::basic_string format( + const text_style &ts, const S &format_str, const Args &... args) { + return internal::vformat( + ts, to_string_view(format_str), + *internal::checked_args(format_str, args...)); +} + #endif FMT_END_NAMESPACE diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index 8d20e776..56f67859 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -206,7 +206,7 @@ TEST(FormatTest, CountCodePoints) { EXPECT_EQ(4, fmt::internal::count_code_points(fmt::u8string_view("ёжик"))); } -TEST(ColorsTest, Colors) { +TEST(ColorsTest, ColorsPrint) { 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(fg(fmt::color::blue), "blue"), @@ -244,3 +244,35 @@ TEST(ColorsTest, Colors) { fmt::print(bg(fmt::terminal_color::bright_magenta), "tbmagenta"), "\x1b[105mtbmagenta\x1b[0m"); } + +TEST(ColorsTest, ColorsFormat) { + EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"), + "\x1b[38;2;000;000;255mblue\x1b[0m"); + EXPECT_EQ(fmt::format(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_EQ(fmt::format(fmt::emphasis::bold, "bold"), + "\x1b[1mbold\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"), + "\x1b[3mitalic\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"), + "\x1b[4munderline\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"), + "\x1b[9mstrikethrough\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"), + "\x1b[1mbold error\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue log"), + "\x1b[38;2;000;000;255mblue log\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::text_style(), "hi"), "hi"); + EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "tred"), + "\x1b[31mtred\x1b[0m"); + EXPECT_EQ(fmt::format(bg(fmt::terminal_color::cyan), "tcyan"), + "\x1b[46mtcyan\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::terminal_color::bright_green), "tbgreen"), + "\x1b[92mtbgreen\x1b[0m"); + EXPECT_EQ(fmt::format(bg(fmt::terminal_color::bright_magenta), "tbmagenta"), + "\x1b[105mtbmagenta\x1b[0m"); +}