diff --git a/sjsonnet/src/sjsonnet/Format.scala b/sjsonnet/src/sjsonnet/Format.scala index 77377dac..c8a30990 100644 --- a/sjsonnet/src/sjsonnet/Format.scala +++ b/sjsonnet/src/sjsonnet/Format.scala @@ -286,7 +286,13 @@ object Format { else if (signedConversion && formatted.signCharacter) "+" + lhs else lhs - val missingWidth = formatted.widthOr(-1) - lhs2.length - mhs.length - rhs.length + val missingWidth = + if (numeric) formatted.widthOr(-1) - lhs2.length - mhs.length - rhs.length + else + formatted.widthOr(-1) - + lhs2.codePointCount(0, lhs2.length) - + mhs.codePointCount(0, mhs.length) - + rhs.codePointCount(0, rhs.length) if (missingWidth <= 0) { // Avoid unnecessary string concatenation when parts are empty diff --git a/sjsonnet/test/resources/new_test_suite/format_width_codepoint.jsonnet b/sjsonnet/test/resources/new_test_suite/format_width_codepoint.jsonnet new file mode 100644 index 00000000..bb469103 --- /dev/null +++ b/sjsonnet/test/resources/new_test_suite/format_width_codepoint.jsonnet @@ -0,0 +1,11 @@ +// Width padding must count codepoints, not UTF-16 code units. +// Supplementary characters (surrogate pairs) count as 1 codepoint. +[ + std.assertEqual("%5s" % "😀", " 😀"), + std.assertEqual("%-5s" % "😀", "😀 "), + std.assertEqual("%5s" % "a😀b", " a😀b"), + std.assertEqual("%3s" % "😀😀", " 😀😀"), + std.assertEqual("%5.1s" % "😀x", " 😀"), + std.assertEqual("%5s" % "hello", "hello"), + std.assertEqual("%10s" % "hello", " hello"), +] diff --git a/sjsonnet/test/resources/new_test_suite/format_width_codepoint.jsonnet.golden b/sjsonnet/test/resources/new_test_suite/format_width_codepoint.jsonnet.golden new file mode 100644 index 00000000..15f719b5 --- /dev/null +++ b/sjsonnet/test/resources/new_test_suite/format_width_codepoint.jsonnet.golden @@ -0,0 +1,9 @@ +[ + true, + true, + true, + true, + true, + true, + true +]