Skip to content

Commit

Permalink
Implement styled arguments (#2793)
Browse files Browse the repository at this point in the history
* Implement styled arguments

* Inherit from formatter<Arg> 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
  • Loading branch information
rbrugo authored Mar 8, 2022
1 parent 5bc39d3 commit 8c9bc07
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
58 changes: 58 additions & 0 deletions include/fmt/color.h
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,11 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
buffer.append(reset_color.begin(), reset_color.end());
}

template <typename T> struct styled_arg {
const T& value;
text_style style;
};

template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
Expand Down Expand Up @@ -630,6 +635,59 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
}

template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& 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<Char>(ts.get_emphasis());
out = std::copy(emphasis.begin(), emphasis.end(), out);
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out);
}
if (ts.has_background()) {
has_style = true;
auto background =
detail::make_background_color<Char>(ts.get_background());
out = std::copy(background.begin(), background.end(), out);
}
out = formatter<T, Char>::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 <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
-> detail::styled_arg<remove_cvref_t<T>> {
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
}

FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE

Expand Down
6 changes: 6 additions & 0 deletions test/color-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

2 comments on commit 8c9bc07

@ssbssa
Copy link

@ssbssa ssbssa commented on 8c9bc07 Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format specifier {s:.2f} of this example doesn't work, see https://godbolt.org/z/G8n6WWT6x, should there maybe be {0:.2f} like in the other examples?

@vitaut
Copy link
Contributor

@vitaut vitaut commented on 8c9bc07 Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ssbssa, good catch, thanks! Fixed in 6452e3c.

Please sign in to comment.