Przeglądaj źródła

AK+Format: Support all format specifiers for strings.

The following is now possible:

    outf("{:.4}", "abcdef"); // abcd
    outf("{:*<8}", "abcdef"); // abcdef**
asynts 4 lat temu
rodzic
commit
71b7ef0992
3 zmienionych plików z 58 dodań i 9 usunięć
  1. 49 8
      AK/Format.cpp
  2. 1 1
      AK/PrintfImplementation.h
  3. 8 0
      AK/Tests/TestFormat.cpp

+ 49 - 8
AK/Format.cpp

@@ -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();
+
+    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;
+    }
 
-    builder.append(value);
+    ASSERT_NOT_REACHED();
 }
 
 template<typename T>

+ 1 - 1
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<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;

+ 8 - 0
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)