AK+Format: Support all format specifiers for strings.

The following is now possible:

    outf("{:.4}", "abcdef"); // abcd
    outf("{:*<8}", "abcdef"); // abcdef**
This commit is contained in:
asynts 2020-09-29 13:55:58 +02:00 committed by Andreas Kling
parent c0d9daadb0
commit 71b7ef0992
3 changed files with 58 additions and 9 deletions

View file

@ -300,24 +300,65 @@ void StandardFormatter::parse(FormatterContext& context)
ASSERT(parser.is_eof());
}
void Formatter<StringView>::format(StringBuilder& builder, StringView value, FormatterContext&)
void Formatter<StringView>::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<char>(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<size_t, size_t>(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<typename T>

View file

@ -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<size_t>(max<ssize_t>(0, static_cast<ssize_t>(width) - static_cast<ssize_t>(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;

View file

@ -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)