From 41b242e869c00b3c200c92a238ef1c5766f5610c Mon Sep 17 00:00:00 2001 From: rbrugo Date: Wed, 2 Mar 2022 23:33:07 +0100 Subject: [PATCH 1/9] 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 6d794c59e557..179c6f6433f4 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 af8f14942603..2bb6a4a337e4 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) { From c9b14e9bd4964667828e39b263581628a5064e6b Mon Sep 17 00:00:00 2001 From: rbrugo Date: Mon, 7 Mar 2022 16:12:15 +0100 Subject: [PATCH 2/9] Inherit from formatter to get the underlying `parse` and `format` --- include/fmt/color.h | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 179c6f6433f4..c9c5a93c133f 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -643,22 +643,7 @@ template struct styled_arg { 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); - } - +struct formatter, Char> : formatter { template auto format(detail::styled_arg const& arg, FormatContext& ctx) const -> decltype(ctx.out()) { @@ -687,7 +672,7 @@ struct formatter, Char> { detail::make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } - out = value_formatter_.format(value, ctx); + out = formatter::format(value, ctx); if (has_style) detail::reset_color(buf); return out; } From 406accd17e0d0b9c87008eff0467b6a0b3d376da Mon Sep 17 00:00:00 2001 From: rbrugo Date: Mon, 7 Mar 2022 16:49:59 +0100 Subject: [PATCH 3/9] Move styled_arg definition into the previous detail block --- include/fmt/color.h | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index c9c5a93c133f..7fa830072da6 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -492,6 +492,14 @@ template inline void reset_color(buffer& buffer) { buffer.append(reset_color.begin(), reset_color.end()); } +template struct styled_arg { + FMT_CONSTEXPR styled_arg(Arg const& argument, text_style style) + : argument(argument), style(style) {} + + const Arg& argument; + text_style style; +}; + template void vformat_to(buffer& buf, const text_style& ts, basic_string_view format_str, @@ -630,18 +638,6 @@ 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> : formatter { template From 1fc4d8bb70a8a4fb1fa2190f709ed82ae420f0c5 Mon Sep 17 00:00:00 2001 From: rbrugo Date: Mon, 7 Mar 2022 18:59:04 +0100 Subject: [PATCH 4/9] Change styled_arg ctor parameters names to avoid shadowing members --- include/fmt/color.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 7fa830072da6..fb8af9026808 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -493,8 +493,8 @@ template inline void reset_color(buffer& buffer) { } template struct styled_arg { - FMT_CONSTEXPR styled_arg(Arg const& argument, text_style style) - : argument(argument), style(style) {} + FMT_CONSTEXPR styled_arg(Arg const& format_argument, text_style format_style) + : argument(format_argument), style(format_style) {} const Arg& argument; text_style style; From 91a9f960047a40067aeca7d8595109d401c38b30 Mon Sep 17 00:00:00 2001 From: rbrugo Date: Mon, 7 Mar 2022 19:40:54 +0100 Subject: [PATCH 5/9] Move const before auto --- include/fmt/color.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index fb8af9026808..0c25bf4f9d05 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -493,7 +493,7 @@ template inline void reset_color(buffer& buffer) { } template struct styled_arg { - FMT_CONSTEXPR styled_arg(Arg const& format_argument, text_style format_style) + FMT_CONSTEXPR styled_arg(const Arg& format_argument, text_style format_style) : argument(format_argument), style(format_style) {} const Arg& argument; @@ -641,10 +641,10 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, template struct formatter, Char> : formatter { template - auto format(detail::styled_arg const& arg, FormatContext& ctx) const + auto format(const detail::styled_arg& arg, FormatContext& ctx) const -> decltype(ctx.out()) { - auto const& ts = arg.style; - auto const& value = arg.argument; + const auto& ts = arg.style; + const auto& value = arg.argument; auto out = ctx.out(); using detail::get_buffer; From ad95044f251b18862fb1fda7fffc9083d33c3433 Mon Sep 17 00:00:00 2001 From: rbrugo Date: Tue, 8 Mar 2022 00:11:22 +0100 Subject: [PATCH 6/9] Remove redundant constructor for styled_arg --- include/fmt/color.h | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 0c25bf4f9d05..e5a487bf0e88 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -492,11 +492,8 @@ template inline void reset_color(buffer& buffer) { buffer.append(reset_color.begin(), reset_color.end()); } -template struct styled_arg { - FMT_CONSTEXPR styled_arg(const Arg& format_argument, text_style format_style) - : argument(format_argument), style(format_style) {} - - const Arg& argument; +template struct styled_arg { + const T& value; text_style style; }; @@ -638,13 +635,13 @@ 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 +struct formatter, Char> : formatter { template - auto format(const detail::styled_arg& arg, FormatContext& ctx) const + auto format(const detail::styled_arg& arg, FormatContext& ctx) const -> decltype(ctx.out()) { const auto& ts = arg.style; - const auto& value = arg.argument; + const auto& value = arg.value; auto out = ctx.out(); using detail::get_buffer; @@ -668,7 +665,7 @@ struct formatter, Char> : formatter { detail::make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } - out = formatter::format(value, ctx); + out = formatter::format(value, ctx); if (has_style) detail::reset_color(buf); return out; } @@ -685,10 +682,10 @@ struct formatter, Char> : formatter { 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); +template +FMT_CONSTEXPR auto styled(const T& value, text_style ts = {}) + -> detail::styled_arg> { + return detail::styled_arg>{value, ts}; } /** @@ -701,10 +698,10 @@ FMT_CONSTEXPR auto styled(const Arg& arg, text_style ts = {}) 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)); +template +FMT_CONSTEXPR auto styled(const T& value, detail::color_type color) + -> detail::styled_arg> { + return detail::styled_arg>{value, fg(color)}; } /** @@ -717,10 +714,10 @@ FMT_CONSTEXPR auto styled(const Arg& arg, detail::color_type color) 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)); +template +FMT_CONSTEXPR auto styled(const T& value, emphasis em) + -> detail::styled_arg> { + return detail::styled_arg>{value, text_style(em)}; } FMT_MODULE_EXPORT_END From 1b663051b5603cb9f64735f3f35efabbf7d434f3 Mon Sep 17 00:00:00 2001 From: rbrugo Date: Tue, 8 Mar 2022 16:11:07 +0100 Subject: [PATCH 7/9] Use the iterator instead of the buffer in styled_arg::format --- include/fmt/color.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index e5a487bf0e88..cf7010020282 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -644,29 +644,29 @@ struct formatter, Char> : formatter { const auto& value = arg.value; 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()); + out = std::copy(emphasis.begin(), emphasis.end(), out); } if (ts.has_foreground()) { has_style = true; auto foreground = detail::make_foreground_color(ts.get_foreground()); - buf.append(foreground.begin(), foreground.end()); + out = std::copy(foreground.begin(), foreground.end(), out); } if (ts.has_background()) { has_style = true; auto background = detail::make_background_color(ts.get_background()); - buf.append(background.begin(), background.end()); + out = std::copy(background.begin(), background.end(), out); } out = formatter::format(value, ctx); - if (has_style) detail::reset_color(buf); + if (has_style) { + auto reset_color = string_view("\x1b[0m"); + out = std::copy(reset_color.begin(), reset_color.end(), out); + } return out; } }; From b250c639bfd6c650c8d1b350fa63f47fe92121ef Mon Sep 17 00:00:00 2001 From: rbrugo Date: Tue, 8 Mar 2022 16:15:42 +0100 Subject: [PATCH 8/9] Remove unnecessary `styled` overloads --- include/fmt/color.h | 36 ++---------------------------------- test/color-test.cc | 2 +- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index cf7010020282..9ee0da359873 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -679,8 +679,8 @@ struct formatter, Char> : formatter { **Example**:: fmt::print("Elapsed time: {s:.2f} seconds", - fmt::styled(1.23, fmt::fg(fmt::colors::green) | - fmt::bg(fmt::color::blue))); \endrst + 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 = {}) @@ -688,38 +688,6 @@ FMT_CONSTEXPR auto styled(const T& value, text_style ts = {}) return detail::styled_arg>{value, 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 T& value, detail::color_type color) - -> detail::styled_arg> { - return detail::styled_arg>{value, 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 T& value, emphasis em) - -> detail::styled_arg> { - return detail::styled_arg>{value, text_style(em)}; -} - FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/test/color-test.cc b/test/color-test.cc index 2bb6a4a337e4..c2ba13a977db 100644 --- a/test/color-test.cc +++ b/test/color-test.cc @@ -50,7 +50,7 @@ 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), + 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) | From 0b07acfa807bc52700f987e0dba10533fc12dc97 Mon Sep 17 00:00:00 2001 From: rbrugo Date: Tue, 8 Mar 2022 17:11:28 +0100 Subject: [PATCH 9/9] Remove defaulted text_style parameter in styled function --- include/fmt/color.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 9ee0da359873..8e26dfa37779 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -683,7 +683,7 @@ struct formatter, Char> : formatter { \endrst */ template -FMT_CONSTEXPR auto styled(const T& value, text_style ts = {}) +FMT_CONSTEXPR auto styled(const T& value, text_style ts) -> detail::styled_arg> { return detail::styled_arg>{value, ts}; }