From 6d79757a3804658f692e21fb57d32972a1371c13 Mon Sep 17 00:00:00 2001 From: Nikhil <77785504+nikhilreddydev@users.noreply.github.com> Date: Sun, 25 May 2025 21:12:47 +0530 Subject: [PATCH] Interpret precision as display width (#4443) --- include/fmt/format.h | 42 ++++++++++++++++++++++++++++++++++++++++-- test/format-test.cc | 5 +++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 3c62b947..58c7cd79 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2109,13 +2109,51 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, return write_int(out, make_write_int_arg(value, specs.sign()), specs); } +FMT_INLINE auto count_code_points_with_display_width_precision( + string_view s, size_t display_width_precision) -> size_t { + size_t display_width = 0; + size_t code_points = 0; + + // Iterate through the string to compute display width + for_each_codepoint(s, [&](uint32_t, string_view sv) { + // Compute the display width of the current code point + size_t cp_width = compute_width(sv); + if (display_width + cp_width > display_width_precision) { + return false; // Stop iteration when display width exceeds precision + } + + display_width += cp_width; + code_points++; + return true; + }); + + return code_points; +} + +template +FMT_CONSTEXPR auto handle_precision( + basic_string_view s, const format_specs& specs, + FMT_ENABLE_IF(std::is_same::value)) -> size_t { + auto code_points = count_code_points_with_display_width_precision( + s, to_unsigned(specs.precision)); + return code_point_index(s, code_points); +} + +template +FMT_CONSTEXPR auto handle_precision( + basic_string_view s, const format_specs&, + FMT_ENABLE_IF(!std::is_same::value)) -> size_t { + return code_point_index(s, s.size()); +} + template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); - if (specs.precision >= 0 && to_unsigned(specs.precision) < size) - size = code_point_index(s, to_unsigned(specs.precision)); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) { + size = handle_precision(s, specs); + } bool is_debug = specs.type() == presentation_type::debug; if (is_debug) { diff --git a/test/format-test.cc b/test/format-test.cc index 7ee70477..73f81c1d 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -6,6 +6,7 @@ // For the license information refer to format.h. // Check if fmt/format.h compiles with windows.h included before it. +#include #ifdef _WIN32 # include #endif @@ -552,6 +553,10 @@ TEST(format_test, arg_errors) { format_error, "argument not found"); } +TEST(format_test, display_width_precision) { + EXPECT_EQ(fmt::format("{:.5}", "🐱🐱🐱"), "🐱🐱"); +} + template struct test_format { template static auto format(fmt::string_view fmt, const T&... args) -> std::string {