From 71b7ef0992b5d18bd4d463943a5ab3f3876ce41d Mon Sep 17 00:00:00 2001 From: asynts Date: Tue, 29 Sep 2020 13:55:58 +0200 Subject: [PATCH] AK+Format: Support all format specifiers for strings. The following is now possible: outf("{:.4}", "abcdef"); // abcd outf("{:*<8}", "abcdef"); // abcdef** --- AK/Format.cpp | 57 +++++++++++++++++++++++++++++++++------ AK/PrintfImplementation.h | 2 +- AK/Tests/TestFormat.cpp | 8 ++++++ 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/AK/Format.cpp b/AK/Format.cpp index aaa556d00e..a72c0874a0 100644 --- a/AK/Format.cpp +++ b/AK/Format.cpp @@ -300,24 +300,65 @@ void StandardFormatter::parse(FormatterContext& context) ASSERT(parser.is_eof()); } -void Formatter::format(StringBuilder& builder, StringView value, FormatterContext&) +void Formatter::format(StringBuilder& builder, StringView value, FormatterContext& context) { - if (m_align != Align::Default) - TODO(); if (m_sign != Sign::Default) ASSERT_NOT_REACHED(); if (m_alternative_form) ASSERT_NOT_REACHED(); if (m_zero_pad) ASSERT_NOT_REACHED(); - if (m_width != value_not_set) - TODO(); - if (m_precision != value_not_set) - TODO(); if (m_mode != Mode::Default && m_mode != Mode::String) ASSERT_NOT_REACHED(); + if (m_width != value_not_set && m_precision != value_not_set) + ASSERT_NOT_REACHED(); - builder.append(value); + if (m_align == Align::Default) + m_align = Align::Left; + + const auto width = decode_value(m_width, context); + const auto precision = decode_value(m_precision, context); + + const auto put_padding = [&](size_t amount, char fill) { + for (size_t i = 0; i < amount; ++i) + builder.append(fill); + }; + const auto put_bytes = [&](ReadonlyBytes bytes) { + for (size_t i = 0; i < bytes.size(); ++i) + builder.append(static_cast(bytes[i])); + }; + + auto used_by_string = value.length(); + if (precision != value_not_set) + used_by_string = min(used_by_string, precision); + + const auto used_by_padding = width < used_by_string ? 0 : width - used_by_string; + + if (m_align == Align::Left) { + const auto used_by_right_padding = used_by_padding; + + put_bytes(value.bytes().trim(used_by_string)); + put_padding(used_by_right_padding, m_fill); + return; + } + if (m_align == Align::Center) { + const auto used_by_left_padding = used_by_padding / 2; + const auto used_by_right_padding = ceil_div(used_by_padding, 2); + + put_padding(used_by_left_padding, m_fill); + put_bytes(value.bytes().trim(used_by_string)); + put_padding(used_by_right_padding, m_fill); + return; + } + if (m_align == Align::Right) { + const auto used_by_left_padding = used_by_padding; + + put_padding(used_by_left_padding, m_fill); + put_bytes(value.bytes().trim(used_by_string)); + return; + } + + ASSERT_NOT_REACHED(); } template diff --git a/AK/PrintfImplementation.h b/AK/PrintfImplementation.h index 4fd8665ece..f7926968c2 100644 --- a/AK/PrintfImplementation.h +++ b/AK/PrintfImplementation.h @@ -141,7 +141,7 @@ inline size_t convert_unsigned_to_string( }; const auto used_by_field = used_by_significant_digits + used_by_prefix; - const auto used_by_padding = static_cast(max(0, static_cast(width) - static_cast(used_by_field))); + const auto used_by_padding = width < used_by_field ? 0 : width - used_by_field; if (align == Align::Left) { const auto used_by_right_padding = used_by_padding; diff --git a/AK/Tests/TestFormat.cpp b/AK/Tests/TestFormat.cpp index 898fd49df0..18994250fd 100644 --- a/AK/Tests/TestFormat.cpp +++ b/AK/Tests/TestFormat.cpp @@ -122,4 +122,12 @@ TEST_CASE(replacement_field) EXPECT_EQ(String::formatted("{:0{}}", 1, 3), "001"); } +TEST_CASE(complex_string_specifiers) +{ + EXPECT_EQ(String::formatted("{:.8}", "123456789"), "12345678"); + EXPECT_EQ(String::formatted("{:9}", "abcd"), "abcd "); + EXPECT_EQ(String::formatted("{:>9}", "abcd"), " abcd"); + EXPECT_EQ(String::formatted("{:^9}", "abcd"), " abcd "); +} + TEST_MAIN(Format)