From 41b242e869c00b3c200c92a238ef1c5766f5610c Mon Sep 17 00:00:00 2001 From: rbrugo Date: Wed, 2 Mar 2022 23:33:07 +0100 Subject: [PATCH] Implement styled arguments --- include/fmt/color.h | 112 ++++++++++++++++++++++++++++++++++++++++++++ test/color-test.cc | 6 +++ 2 files changed, 118 insertions(+) diff --git a/include/fmt/color.h b/include/fmt/color.h index 6d794c59..179c6f64 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -630,6 +630,118 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, fmt::make_format_args>>(args...)); } +FMT_BEGIN_DETAIL_NAMESPACE + +template struct styled_arg { + FMT_CONSTEXPR styled_arg(Arg const& argument, text_style style) + : argument(argument), style(style) {} + + const Arg& argument; + text_style style; +}; + +FMT_END_DETAIL_NAMESPACE + +template +struct formatter, Char> { + private: + using value_type = Arg; + using formatter_type = + conditional_t::value, + formatter, Char>, + detail::fallback_formatter>; + + formatter_type value_formatter_; + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return value_formatter_.parse(ctx); + } + + template + auto format(detail::styled_arg const& arg, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto const& ts = arg.style; + auto const& value = arg.argument; + auto out = ctx.out(); + + using detail::get_buffer; + auto&& buf = get_buffer(out); + + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = + detail::make_foreground_color(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + has_style = true; + auto background = + detail::make_background_color(ts.get_background()); + buf.append(background.begin(), background.end()); + } + out = value_formatter_.format(value, ctx); + if (has_style) detail::reset_color(buf); + 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 Arg& arg, text_style ts = {}) + -> detail::styled_arg> { + return detail::styled_arg>(arg, ts); +} + +/** + \rst + Returns an argument surrounded by the ANSI escape sequences of the color, + to be used in a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::styled(1.23, + fmt::colors::green)); \endrst + */ +template +FMT_CONSTEXPR auto styled(const Arg& arg, detail::color_type color) + -> detail::styled_arg> { + return detail::styled_arg>(arg, fg(color)); +} + +/** + \rst + Returns an argument associated with an emphasis, to be used + in a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::styled(1.23, + fmt::emphasis::italic)); \endrst + */ +template +FMT_CONSTEXPR auto styled(const Arg& arg, emphasis em) + -> detail::styled_arg> { + return detail::styled_arg>(arg, text_style(em)); +} + FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/test/color-test.cc b/test/color-test.cc index af8f1494..2bb6a4a3 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", 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) {