From 8c9bc070f5d593ca9e6335aa8989c92a91268957 Mon Sep 17 00:00:00 2001 From: Riccardo Brugo Date: Tue, 8 Mar 2022 18:50:14 +0100 Subject: [PATCH] Implement styled arguments (#2793) * Implement styled arguments * Inherit from formatter to get the underlying `parse` and `format` * Move styled_arg definition into the previous detail block * Change styled_arg ctor parameters names to avoid shadowing members * Move const before auto * Remove redundant constructor for styled_arg * Use the iterator instead of the buffer in styled_arg::format * Remove unnecessary `styled` overloads * Remove defaulted text_style parameter in styled function --- include/fmt/color.h | 58 +++++++++++++++++++++++++++++++++++++++++++++ test/color-test.cc | 6 +++++ 2 files changed, 64 insertions(+) diff --git a/include/fmt/color.h b/include/fmt/color.h index 6d794c59..8e26dfa3 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -492,6 +492,11 @@ template inline void reset_color(buffer& buffer) { buffer.append(reset_color.begin(), reset_color.end()); } +template struct styled_arg { + const T& value; + text_style style; +}; + template void vformat_to(buffer& buf, const text_style& ts, basic_string_view format_str, @@ -630,6 +635,59 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, fmt::make_format_args>>(args...)); } +template +struct formatter, Char> : formatter { + template + auto format(const detail::styled_arg& arg, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto& ts = arg.style; + const auto& value = arg.value; + auto out = ctx.out(); + + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + out = std::copy(emphasis.begin(), emphasis.end(), out); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = + detail::make_foreground_color(ts.get_foreground()); + out = std::copy(foreground.begin(), foreground.end(), out); + } + if (ts.has_background()) { + has_style = true; + auto background = + detail::make_background_color(ts.get_background()); + out = std::copy(background.begin(), background.end(), out); + } + out = formatter::format(value, ctx); + if (has_style) { + auto reset_color = string_view("\x1b[0m"); + out = std::copy(reset_color.begin(), reset_color.end(), out); + } + return out; + } +}; + +/** + \rst + Returns an argument that will be formatted using ANSI escape sequences, + to be used in a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", + fmt::styled(1.23, fmt::fg(fmt::colors::green) | fmt::bg(fmt::color::blue))); + \endrst + */ +template +FMT_CONSTEXPR auto styled(const T& value, text_style ts) + -> detail::styled_arg> { + return detail::styled_arg>{value, ts}; +} + FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/test/color-test.cc b/test/color-test.cc index af8f1494..c2ba13a9 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -50,6 +50,12 @@ TEST(color_test, format) { "\x1b[105mtbmagenta\x1b[0m"); EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"), "\x1b[31mfoo\x1b[0m"); + EXPECT_EQ(fmt::format("{}{}", fmt::styled("red", fg(fmt::color::red)), + fmt::styled("bold", fmt::emphasis::bold)), + "\x1b[38;2;255;000;000mred\x1b[0m\x1b[1mbold\x1b[0m"); + EXPECT_EQ(fmt::format("{}", fmt::styled("bar", fg(fmt::color::blue) | + fmt::emphasis::underline)), + "\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m"); } TEST(color_test, format_to) {