11cb0ef41Sopenharmony_ci// Flags: --expose-internals
21cb0ef41Sopenharmony_ci'use strict';
31cb0ef41Sopenharmony_ciconst common = require('../common');
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciconst assert = require('assert');
61cb0ef41Sopenharmony_ciconst { getStringWidth } = require('internal/util/inspect');
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci// Test column width
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ci// Ll (Lowercase Letter): LATIN SMALL LETTER A
111cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('a'), 1);
121cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x0061)), 1);
131cb0ef41Sopenharmony_ci// Lo (Other Letter)
141cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('丁'), 2);
151cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x4E01)), 2);
161cb0ef41Sopenharmony_ci// Surrogate pairs
171cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('\ud83d\udc78\ud83c\udfff'), 4);
181cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('�'), 2);
191cb0ef41Sopenharmony_ci// Cs (Surrogate): High Surrogate
201cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('\ud83d'), 1);
211cb0ef41Sopenharmony_ci// Cs (Surrogate): Low Surrogate
221cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('\udc78'), 1);
231cb0ef41Sopenharmony_ci// Cc (Control): NULL
241cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('\u0000'), 0);
251cb0ef41Sopenharmony_ci// Cc (Control): BELL
261cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x0007)), 0);
271cb0ef41Sopenharmony_ci// Cc (Control): LINE FEED
281cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('\n'), 0);
291cb0ef41Sopenharmony_ci// Cf (Format): SOFT HYPHEN
301cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x00AD)), 1);
311cb0ef41Sopenharmony_ci// Cf (Format): LEFT-TO-RIGHT MARK
321cb0ef41Sopenharmony_ci// Cf (Format): RIGHT-TO-LEFT MARK
331cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('\u200Ef\u200F'), 1);
341cb0ef41Sopenharmony_ci// Cn (Unassigned): Not a character
351cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x10FFEF)), 1);
361cb0ef41Sopenharmony_ci// Cn (Unassigned): Not a character (but in a CJK range)
371cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x3FFEF)), 1);
381cb0ef41Sopenharmony_ci// Mn (Nonspacing Mark): COMBINING ACUTE ACCENT
391cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x0301)), 0);
401cb0ef41Sopenharmony_ci// Mc (Spacing Mark): BALINESE ADEG ADEG
411cb0ef41Sopenharmony_ci// Chosen as its Canonical_Combining_Class is not 0, but is not a 0-width
421cb0ef41Sopenharmony_ci// character.
431cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x1B44)), 1);
441cb0ef41Sopenharmony_ci// Me (Enclosing Mark): COMBINING ENCLOSING CIRCLE
451cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth(String.fromCharCode(0x20DD)), 0);
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci// The following is an emoji sequence with ZWJ (zero-width-joiner). In some
481cb0ef41Sopenharmony_ci// implementations, it is represented as a single glyph, in other
491cb0ef41Sopenharmony_ci// implementations as a sequence of individual glyphs. By default, each
501cb0ef41Sopenharmony_ci// component will be counted individually, since not a lot of systems support
511cb0ef41Sopenharmony_ci// these fully.
521cb0ef41Sopenharmony_ci// See https://www.unicode.org/reports/tr51/tr51-16.html#Emoji_ZWJ_Sequences
531cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('�‍�‍�‍�'), 8);
541cb0ef41Sopenharmony_ci// TODO(BridgeAR): This should have a width of two and six. The heart contains
551cb0ef41Sopenharmony_ci// the \uFE0F variation selector that indicates that it should be displayed as
561cb0ef41Sopenharmony_ci// emoji instead of as text. Emojis are all full width characters when not being
571cb0ef41Sopenharmony_ci// rendered as text.
581cb0ef41Sopenharmony_ci// https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)
591cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('❤️'), 1);
601cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('�‍❤️‍�'), 5);
611cb0ef41Sopenharmony_ci// The length of one is correct. It is an emoji treated as text.
621cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('❤'), 1);
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci// By default, unicode characters whose width is considered ambiguous will
651cb0ef41Sopenharmony_ci// be considered half-width. For these characters, getStringWidth will return
661cb0ef41Sopenharmony_ci// 1. In some contexts, however, it is more appropriate to consider them full
671cb0ef41Sopenharmony_ci// width. By default, the algorithm will assume half width.
681cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('\u01d4'), 1);
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci// Control chars and combining chars are zero
711cb0ef41Sopenharmony_ciassert.strictEqual(getStringWidth('\u200E\n\u220A\u20D2'), 1);
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci// Test that the fast path for ASCII characters yields results consistent
741cb0ef41Sopenharmony_ci// with the 'slow' path.
751cb0ef41Sopenharmony_cifor (let i = 0; i < 256; i++) {
761cb0ef41Sopenharmony_ci  const char = String.fromCharCode(i);
771cb0ef41Sopenharmony_ci  assert.strictEqual(
781cb0ef41Sopenharmony_ci    getStringWidth(char + '�'),
791cb0ef41Sopenharmony_ci    getStringWidth(char) + 2);
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci  if (i < 32 || (i >= 127 && i < 160)) {  // Control character
821cb0ef41Sopenharmony_ci    assert.strictEqual(getStringWidth(char), 0);
831cb0ef41Sopenharmony_ci  } else {  // Regular ASCII character
841cb0ef41Sopenharmony_ci    assert.strictEqual(getStringWidth(char), 1);
851cb0ef41Sopenharmony_ci  }
861cb0ef41Sopenharmony_ci}
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ciif (common.hasIntl) {
891cb0ef41Sopenharmony_ci  const a = '한글'.normalize('NFD'); // 한글
901cb0ef41Sopenharmony_ci  const b = '한글'.normalize('NFC'); // 한글
911cb0ef41Sopenharmony_ci  assert.strictEqual(a.length, 6);
921cb0ef41Sopenharmony_ci  assert.strictEqual(b.length, 2);
931cb0ef41Sopenharmony_ci  assert.strictEqual(getStringWidth(a), 4);
941cb0ef41Sopenharmony_ci  assert.strictEqual(getStringWidth(b), 4);
951cb0ef41Sopenharmony_ci}
96