diff options
Diffstat (limited to 'cli/js')
-rw-r--r-- | cli/js/tests/console_test.ts | 14 | ||||
-rw-r--r-- | cli/js/web/console_table.ts | 59 |
2 files changed, 64 insertions, 9 deletions
diff --git a/cli/js/tests/console_test.ts b/cli/js/tests/console_test.ts index 264987ba9..a49cd95ca 100644 --- a/cli/js/tests/console_test.ts +++ b/cli/js/tests/console_test.ts @@ -1045,6 +1045,20 @@ unitTest(function consoleTable(): void { console.table("test"); assertEquals(out.toString(), "test\n"); }); + mockConsole((console, out): void => { + console.table(["Hello", "你好", "Amapá"]); + assertEquals( + out.toString(), + `┌─────────┬─────────┐ +│ (index) │ Values │ +├─────────┼─────────┤ +│ 0 │ "Hello" │ +│ 1 │ "你好" │ +│ 2 │ "Amapá" │ +└─────────┴─────────┘ +` + ); + }); }); // console.log(Error) test diff --git a/cli/js/web/console_table.ts b/cli/js/web/console_table.ts index 2cb0005d7..dc22433af 100644 --- a/cli/js/web/console_table.ts +++ b/cli/js/web/console_table.ts @@ -1,11 +1,8 @@ // Copyright Joyent, Inc. and other Node contributors. MIT license. // Forked from Node's lib/internal/cli_table.js -import { TextEncoder } from "./text_encoding.ts"; import { hasOwnProperty } from "./util.ts"; -const encoder = new TextEncoder(); - const tableChars = { middleMiddle: "─", rowMiddle: "┼", @@ -28,17 +25,61 @@ function removeColors(str: string): string { return str.replace(colorRegExp, ""); } -function countBytes(str: string): number { - const normalized = removeColors(String(str)).normalize("NFC"); +function isFullWidthCodePoint(code: number): boolean { + // Code points are partially derived from: + // http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt + return ( + code >= 0x1100 && + (code <= 0x115f || // Hangul Jamo + code === 0x2329 || // LEFT-POINTING ANGLE BRACKET + code === 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK Radicals Supplement .. Enclosed CJK Letters and Months + (code >= 0x2e80 && code <= 0x3247 && code !== 0x303f) || + // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A + (code >= 0x3250 && code <= 0x4dbf) || + // CJK Unified Ideographs .. Yi Radicals + (code >= 0x4e00 && code <= 0xa4c6) || + // Hangul Jamo Extended-A + (code >= 0xa960 && code <= 0xa97c) || + // Hangul Syllables + (code >= 0xac00 && code <= 0xd7a3) || + // CJK Compatibility Ideographs + (code >= 0xf900 && code <= 0xfaff) || + // Vertical Forms + (code >= 0xfe10 && code <= 0xfe19) || + // CJK Compatibility Forms .. Small Form Variants + (code >= 0xfe30 && code <= 0xfe6b) || + // Halfwidth and Fullwidth Forms + (code >= 0xff01 && code <= 0xff60) || + (code >= 0xffe0 && code <= 0xffe6) || + // Kana Supplement + (code >= 0x1b000 && code <= 0x1b001) || + // Enclosed Ideographic Supplement + (code >= 0x1f200 && code <= 0x1f251) || + // Miscellaneous Symbols and Pictographs 0x1f300 - 0x1f5ff + // Emoticons 0x1f600 - 0x1f64f + (code >= 0x1f300 && code <= 0x1f64f) || + // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane + (code >= 0x20000 && code <= 0x3fffd)) + ); +} + +function getStringWidth(str: string): number { + str = removeColors(str).normalize("NFC"); + let width = 0; + + for (const ch of str) { + width += isFullWidthCodePoint(ch.codePointAt(0)!) ? 2 : 1; + } - return encoder.encode(normalized).byteLength; + return width; } function renderRow(row: string[], columnWidths: number[]): string { let out = tableChars.left; for (let i = 0; i < row.length; i++) { const cell = row[i]; - const len = countBytes(cell); + const len = getStringWidth(cell); const needed = (columnWidths[i] - len) / 2; // round(needed) + ceil(needed) will always add up to the amount // of spaces we need while also left justifying the output. @@ -53,7 +94,7 @@ function renderRow(row: string[], columnWidths: number[]): string { export function cliTable(head: string[], columns: string[][]): string { const rows: string[][] = []; - const columnWidths = head.map((h: string): number => countBytes(h)); + const columnWidths = head.map((h: string): number => getStringWidth(h)); const longestColumn = columns.reduce( (n: number, a: string[]): number => Math.max(n, a.length), 0 @@ -67,7 +108,7 @@ export function cliTable(head: string[], columns: string[][]): string { } const value = (rows[j][i] = hasOwnProperty(column, j) ? column[j] : ""); const width = columnWidths[i] || 0; - const counted = countBytes(value); + const counted = getStringWidth(value); columnWidths[i] = Math.max(width, counted); } } |