• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals
2'use strict';
3const common = require('../common');
4
5const assert = require('assert');
6const { getStringWidth } = require('internal/util/inspect');
7
8// Test column width
9
10// Ll (Lowercase Letter): LATIN SMALL LETTER A
11assert.strictEqual(getStringWidth('a'), 1);
12assert.strictEqual(getStringWidth(String.fromCharCode(0x0061)), 1);
13// Lo (Other Letter)
14assert.strictEqual(getStringWidth('丁'), 2);
15assert.strictEqual(getStringWidth(String.fromCharCode(0x4E01)), 2);
16// Surrogate pairs
17assert.strictEqual(getStringWidth('\ud83d\udc78\ud83c\udfff'), 4);
18assert.strictEqual(getStringWidth('��'), 2);
19// Cs (Surrogate): High Surrogate
20assert.strictEqual(getStringWidth('\ud83d'), 1);
21// Cs (Surrogate): Low Surrogate
22assert.strictEqual(getStringWidth('\udc78'), 1);
23// Cc (Control): NULL
24assert.strictEqual(getStringWidth('\u0000'), 0);
25// Cc (Control): BELL
26assert.strictEqual(getStringWidth(String.fromCharCode(0x0007)), 0);
27// Cc (Control): LINE FEED
28assert.strictEqual(getStringWidth('\n'), 0);
29// Cf (Format): SOFT HYPHEN
30assert.strictEqual(getStringWidth(String.fromCharCode(0x00AD)), 1);
31// Cf (Format): LEFT-TO-RIGHT MARK
32// Cf (Format): RIGHT-TO-LEFT MARK
33assert.strictEqual(getStringWidth('\u200Ef\u200F'), 1);
34// Cn (Unassigned): Not a character
35assert.strictEqual(getStringWidth(String.fromCharCode(0x10FFEF)), 1);
36// Cn (Unassigned): Not a character (but in a CJK range)
37assert.strictEqual(getStringWidth(String.fromCharCode(0x3FFEF)), 1);
38// Mn (Nonspacing Mark): COMBINING ACUTE ACCENT
39assert.strictEqual(getStringWidth(String.fromCharCode(0x0301)), 0);
40// Mc (Spacing Mark): BALINESE ADEG ADEG
41// Chosen as its Canonical_Combining_Class is not 0, but is not a 0-width
42// character.
43assert.strictEqual(getStringWidth(String.fromCharCode(0x1B44)), 1);
44// Me (Enclosing Mark): COMBINING ENCLOSING CIRCLE
45assert.strictEqual(getStringWidth(String.fromCharCode(0x20DD)), 0);
46
47// The following is an emoji sequence with ZWJ (zero-width-joiner). In some
48// implementations, it is represented as a single glyph, in other
49// implementations as a sequence of individual glyphs. By default, each
50// component will be counted individually, since not a lot of systems support
51// these fully.
52// See https://www.unicode.org/reports/tr51/tr51-16.html#Emoji_ZWJ_Sequences
53assert.strictEqual(getStringWidth('��‍��‍��‍��'), 8);
54// TODO(BridgeAR): This should have a width of two and six. The heart contains
55// the \uFE0F variation selector that indicates that it should be displayed as
56// emoji instead of as text. Emojis are all full width characters when not being
57// rendered as text.
58// https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)
59assert.strictEqual(getStringWidth('❤️'), 1);
60assert.strictEqual(getStringWidth('��‍❤️‍��'), 5);
61// The length of one is correct. It is an emoji treated as text.
62assert.strictEqual(getStringWidth('❤'), 1);
63
64// By default, unicode characters whose width is considered ambiguous will
65// be considered half-width. For these characters, getStringWidth will return
66// 1. In some contexts, however, it is more appropriate to consider them full
67// width. By default, the algorithm will assume half width.
68assert.strictEqual(getStringWidth('\u01d4'), 1);
69
70// Control chars and combining chars are zero
71assert.strictEqual(getStringWidth('\u200E\n\u220A\u20D2'), 1);
72
73// Test that the fast path for ASCII characters yields results consistent
74// with the 'slow' path.
75for (let i = 0; i < 256; i++) {
76  const char = String.fromCharCode(i);
77  assert.strictEqual(
78    getStringWidth(char + '��'),
79    getStringWidth(char) + 2);
80
81  if (i < 32 || (i >= 127 && i < 160)) {  // Control character
82    assert.strictEqual(getStringWidth(char), 0);
83  } else {  // Regular ASCII character
84    assert.strictEqual(getStringWidth(char), 1);
85  }
86}
87
88if (common.hasIntl) {
89  const a = '한글'.normalize('NFD'); // 한글
90  const b = '한글'.normalize('NFC'); // 한글
91  assert.strictEqual(a.length, 6);
92  assert.strictEqual(b.length, 2);
93  assert.strictEqual(getStringWidth(a), 4);
94  assert.strictEqual(getStringWidth(b), 4);
95}
96