Skip to content

Commit e0e8a9a

Browse files
addaleaxtargos
authored andcommitted
util,readline: NFC-normalize strings before getStringWidth
The assumption here is that decomposed characters render like their composed character equivalents, and that working with the former comes with a risk of over-estimating string widths given that we compute them on a per-code-point basis. The regression test added here (한글 vs 한글) is an example of that happening. PR-URL: #33052 Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Anto Aravinth <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent d217b79 commit e0e8a9a

File tree

2 files changed

+19
-5
lines changed

2 files changed

+19
-5
lines changed

lib/internal/util/inspect.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -1914,6 +1914,13 @@ function formatWithOptions(inspectOptions, ...args) {
19141914
return str;
19151915
}
19161916

1917+
function prepareStringForGetStringWidth(str, removeControlChars) {
1918+
str = str.normalize('NFC');
1919+
if (removeControlChars)
1920+
str = stripVTControlCharacters(str);
1921+
return str;
1922+
}
1923+
19171924
if (internalBinding('config').hasIntl) {
19181925
const icu = internalBinding('icu');
19191926
// icu.getStringWidth(string, ambiguousAsFullWidth, expandEmojiSequence)
@@ -1923,8 +1930,8 @@ if (internalBinding('config').hasIntl) {
19231930
// the receiving end supports.
19241931
getStringWidth = function getStringWidth(str, removeControlChars = true) {
19251932
let width = 0;
1926-
if (removeControlChars)
1927-
str = stripVTControlCharacters(str);
1933+
1934+
str = prepareStringForGetStringWidth(str, removeControlChars);
19281935
for (let i = 0; i < str.length; i++) {
19291936
// Try to avoid calling into C++ by first handling the ASCII portion of
19301937
// the string. If it is fully ASCII, we skip the C++ part.
@@ -1944,9 +1951,7 @@ if (internalBinding('config').hasIntl) {
19441951
getStringWidth = function getStringWidth(str, removeControlChars = true) {
19451952
let width = 0;
19461953

1947-
if (removeControlChars)
1948-
str = stripVTControlCharacters(str);
1949-
1954+
str = prepareStringForGetStringWidth(str, removeControlChars);
19501955
for (const char of str) {
19511956
const code = char.codePointAt(0);
19521957
if (isFullWidthCodePoint(code)) {

test/parallel/test-icu-stringwidth.js

+9
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,12 @@ for (let i = 0; i < 256; i++) {
8787
assert.strictEqual(getStringWidth(char), 1);
8888
}
8989
}
90+
91+
{
92+
const a = '한글'.normalize('NFD'); // 한글
93+
const b = '한글'.normalize('NFC'); // 한글
94+
assert.strictEqual(a.length, 6);
95+
assert.strictEqual(b.length, 2);
96+
assert.strictEqual(getStringWidth(a), 4);
97+
assert.strictEqual(getStringWidth(b), 4);
98+
}

0 commit comments

Comments
 (0)