diff --git a/include/fmt/color.h b/include/fmt/color.h index f1b1f753f84a..25b0fb5db282 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -463,14 +463,14 @@ template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { } template -inline void reset_color(basic_memory_buffer& buffer) FMT_NOEXCEPT { +inline void reset_color(buffer& buffer) FMT_NOEXCEPT { const char* begin = data::reset_color; const char* end = begin + sizeof(data::reset_color) - 1; buffer.append(begin, end); } template -void vformat_to(basic_memory_buffer& buf, const text_style& ts, +void vformat_to(buffer& buf, const text_style& ts, basic_string_view format_str, basic_format_args> args) { bool has_style = false; @@ -563,6 +563,51 @@ inline std::basic_string format(const text_style& ts, const S& format_str, fmt::make_args_checked(format_str, args...)); } +/** Formats a string with the given text_style and writes the output to ``out``. + */ +template , + FMT_ENABLE_IF(detail::is_output_iterator::value)> +OutputIt vformat_to( + OutputIt out, const text_style& ts, const S& format_str, + basic_format_args>> args) { + decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); + detail::vformat_to(buf, ts, to_string_view(format_str), args); + return detail::get_iterator(buf); +} + +/** + \rst + Formats arguments with the given text_style, writes the result to the output + iterator ``out`` and returns the iterator past the end of the output range. + + **Example**:: + + std::vector out; + fmt::format_to(std::back_inserter(out), + fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + \endrst + */ +template ::value&& + detail::is_string::value)> +inline OutputIt format_to(OutputIt out, const text_style& ts, const S& format_str, + Args&&... args) { + basic_format_args>>> store = + fmt::make_args_checked(format_str, args...); + return vformat_to(out, ts, to_string_view(format_str), store); +} + +template ::value, char_t>> +inline typename buffer_context::iterator format_to( + basic_memory_buffer& buf, const text_style& ts, + const S& format_str, Args&&... args) { + basic_format_args>> store = + fmt::make_args_checked(format_str, args...); + detail::vformat_to(buf, ts, to_string_view(format_str), store); + return detail::buffer_appender(buf); +} + FMT_END_NAMESPACE #endif // FMT_COLOR_H_ diff --git a/test/color-test.cc b/test/color-test.cc index 454a0660d4cf..948ae03754e4 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -7,6 +7,10 @@ #include "fmt/color.h" +#include +#include +#include + #include "gtest-extra.h" TEST(ColorsTest, ColorsPrint) { @@ -84,3 +88,106 @@ TEST(ColorsTest, Format) { EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"), "\x1b[31mfoo\x1b[0m"); } + +template < + typename S, typename... Args, + typename String = std::basic_string>>> +String format_to_out_helper(const fmt::text_style& ts, S&& format_str, + Args&&... args) { + String out; + fmt::format_to(std::back_inserter(out), ts, std::forward(format_str), + std::forward(args)...); + + return out; +} + +TEST(ColorsTest, FormatToOut) { + EXPECT_EQ(format_to_out_helper(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_EQ( + format_to_out_helper(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"), + L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fg(fmt::color::blue), "blue"), + "\x1b[38;2;000;000;255mblue\x1b[0m"); + EXPECT_EQ(format_to_out_helper(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(format_to_out_helper(fmt::emphasis::bold, "bold"), + "\x1b[1mbold\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fmt::emphasis::italic, "italic"), + "\x1b[3mitalic\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fmt::emphasis::underline, "underline"), + "\x1b[4munderline\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fmt::emphasis::strikethrough, "strikethrough"), + "\x1b[9mstrikethrough\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fg(fmt::color::blue) | fmt::emphasis::bold, + "blue/bold"), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fmt::emphasis::bold, "bold error"), + "\x1b[1mbold error\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fg(fmt::color::blue), "blue log"), + "\x1b[38;2;000;000;255mblue log\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fmt::text_style(), "hi"), "hi"); + EXPECT_EQ(format_to_out_helper(fg(fmt::terminal_color::red), "tred"), + "\x1b[31mtred\x1b[0m"); + EXPECT_EQ(format_to_out_helper(bg(fmt::terminal_color::cyan), "tcyan"), + "\x1b[46mtcyan\x1b[0m"); + EXPECT_EQ( + format_to_out_helper(fg(fmt::terminal_color::bright_green), "tbgreen"), + "\x1b[92mtbgreen\x1b[0m"); + EXPECT_EQ(format_to_out_helper(bg(fmt::terminal_color::bright_magenta), + "tbmagenta"), + "\x1b[105mtbmagenta\x1b[0m"); + EXPECT_EQ(format_to_out_helper(fg(fmt::terminal_color::red), "{}", "foo"), + "\x1b[31mfoo\x1b[0m"); +} + +template < + typename S, typename... Args, + typename String = std::basic_string>>> +String format_to_helper(const fmt::text_style& ts, S&& format_str, + Args&&... args) { + fmt::basic_memory_buffer>> out; + fmt::format_to(out, ts, std::forward(format_str), + std::forward(args)...); + return String(out.data(), out.data() + out.size()); +} + +TEST(ColorsTest, FormatTo) { + EXPECT_EQ(format_to_helper(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_EQ(format_to_helper(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"), + L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m"); + EXPECT_EQ(format_to_helper(fg(fmt::color::blue), "blue"), + "\x1b[38;2;000;000;255mblue\x1b[0m"); + EXPECT_EQ( + format_to_helper(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(format_to_helper(fmt::emphasis::bold, "bold"), + "\x1b[1mbold\x1b[0m"); + EXPECT_EQ(format_to_helper(fmt::emphasis::italic, "italic"), + "\x1b[3mitalic\x1b[0m"); + EXPECT_EQ(format_to_helper(fmt::emphasis::underline, "underline"), + "\x1b[4munderline\x1b[0m"); + EXPECT_EQ(format_to_helper(fmt::emphasis::strikethrough, "strikethrough"), + "\x1b[9mstrikethrough\x1b[0m"); + EXPECT_EQ( + format_to_helper(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_EQ(format_to_helper(fmt::emphasis::bold, "bold error"), + "\x1b[1mbold error\x1b[0m"); + EXPECT_EQ(format_to_helper(fg(fmt::color::blue), "blue log"), + "\x1b[38;2;000;000;255mblue log\x1b[0m"); + EXPECT_EQ(format_to_helper(fmt::text_style(), "hi"), "hi"); + EXPECT_EQ(format_to_helper(fg(fmt::terminal_color::red), "tred"), + "\x1b[31mtred\x1b[0m"); + EXPECT_EQ(format_to_helper(bg(fmt::terminal_color::cyan), "tcyan"), + "\x1b[46mtcyan\x1b[0m"); + EXPECT_EQ(format_to_helper(fg(fmt::terminal_color::bright_green), "tbgreen"), + "\x1b[92mtbgreen\x1b[0m"); + EXPECT_EQ( + format_to_helper(bg(fmt::terminal_color::bright_magenta), "tbmagenta"), + "\x1b[105mtbmagenta\x1b[0m"); + EXPECT_EQ(format_to_helper(fg(fmt::terminal_color::red), "{}", "foo"), + "\x1b[31mfoo\x1b[0m"); +} diff --git a/test/gtest-extra.h b/test/gtest-extra.h index 3ed8052b3fcb..01c70ddbf06d 100644 --- a/test/gtest-extra.h +++ b/test/gtest-extra.h @@ -145,7 +145,13 @@ std::string read(fmt::file& f, size_t count); read(file, fmt::string_view(expected_content).size())) #else -# define EXPECT_WRITE(file, statement, expected_output) SUCCEED() +# define EXPECT_WRITE(file, statement, expected_output) \ + do { \ + (void)(file); \ + (void)(statement); \ + (void)(expected_output); \ + SUCCEED(); \ + } while (false) #endif // FMT_USE_FCNTL template struct ScopedMock : testing::StrictMock {