Skip to content

Commit 7e61760

Browse files
BridgeARtargos
authored andcommitted
util: improve .inspect() array grouping
This improves a couple minor things: * Arrays that contain entries other than `number` or `bigint` are ordered to the left instead of the right. * The bias towards more columns got increased. That mainly increases the number of columns for arrays that contain lots of short entries. * Columns are now more dense in case they would otherwise have extra whitespace in-between two columns. * The maximum columns got increased from 10 to 15. * The maximum number of columns per `compact` was increased from 3 to 4. PR-URL: #28070 Refs: #27690 Reviewed-By: James M Snell <[email protected]>
1 parent aa3c41f commit 7e61760

File tree

2 files changed

+134
-120
lines changed

2 files changed

+134
-120
lines changed

lib/internal/util/inspect.js

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
777777
}
778778

779779
const res = reduceToSingleString(
780-
ctx, output, base, braces, extrasType, recurseTimes);
780+
ctx, output, base, braces, extrasType, recurseTimes, value);
781781
const budget = ctx.budget[ctx.indentationLvl] || 0;
782782
const newLength = budget + res.length;
783783
ctx.budget[ctx.indentationLvl] = newLength;
@@ -940,7 +940,7 @@ function formatError(err, constructor, tag, ctx) {
940940
return stack;
941941
}
942942

943-
function groupArrayElements(ctx, output) {
943+
function groupArrayElements(ctx, output, value) {
944944
let totalLength = 0;
945945
let maxLength = 0;
946946
let i = 0;
@@ -972,51 +972,73 @@ function groupArrayElements(ctx, output) {
972972
(totalLength / actualMax > 5 || maxLength <= 6)) {
973973

974974
const approxCharHeights = 2.5;
975-
const bias = 1;
975+
const biasedMax = Math.max(actualMax - 4, 1);
976976
// Dynamically check how many columns seem possible.
977977
const columns = Math.min(
978978
// Ideally a square should be drawn. We expect a character to be about 2.5
979979
// times as high as wide. This is the area formula to calculate a square
980980
// which contains n rectangles of size `actualMax * approxCharHeights`.
981981
// Divide that by `actualMax` to receive the correct number of columns.
982-
// The added bias slightly increases the columns for short entries.
982+
// The added bias increases the columns for short entries.
983983
Math.round(
984984
Math.sqrt(
985-
approxCharHeights * (actualMax - bias) * outputLength
986-
) / (actualMax - bias)
985+
approxCharHeights * biasedMax * outputLength
986+
) / biasedMax
987987
),
988988
// Do not exceed the breakLength.
989989
Math.floor((ctx.breakLength - ctx.indentationLvl) / actualMax),
990990
// Limit array grouping for small `compact` modes as the user requested
991991
// minimal grouping.
992-
ctx.compact * 3,
993-
// Limit the columns to a maximum of ten.
994-
10
992+
ctx.compact * 4,
993+
// Limit the columns to a maximum of fifteen.
994+
15
995995
);
996996
// Return with the original output if no grouping should happen.
997997
if (columns <= 1) {
998998
return output;
999999
}
1000-
// Calculate the maximum length of all entries that are visible in the first
1001-
// column of the group.
10021000
const tmp = [];
1003-
let firstLineMaxLength = dataLen[0];
1004-
for (i = columns; i < dataLen.length; i += columns) {
1005-
if (dataLen[i] > firstLineMaxLength)
1006-
firstLineMaxLength = dataLen[i];
1001+
const maxLineLength = [];
1002+
for (let i = 0; i < columns; i++) {
1003+
let lineMaxLength = 0;
1004+
for (let j = i; j < output.length; j += columns) {
1005+
if (dataLen[j] > lineMaxLength)
1006+
lineMaxLength = dataLen[j];
1007+
}
1008+
lineMaxLength += separatorSpace;
1009+
maxLineLength[i] = lineMaxLength;
1010+
}
1011+
let order = 'padStart';
1012+
if (value !== undefined) {
1013+
for (let i = 0; i < output.length; i++) {
1014+
// eslint-disable-next-line valid-typeof
1015+
if (typeof value[i] !== 'number' && typeof value[i] !== 'bigint') {
1016+
order = 'padEnd';
1017+
break;
1018+
}
1019+
}
10071020
}
10081021
// Each iteration creates a single line of grouped entries.
1009-
for (i = 0; i < outputLength; i += columns) {
1010-
// Calculate extra color padding in case it's active. This has to be done
1011-
// line by line as some lines might contain more colors than others.
1012-
let colorPadding = output[i].length - dataLen[i];
1013-
// Add padding to the first column of the output.
1014-
let str = output[i].padStart(firstLineMaxLength + colorPadding, ' ');
1022+
for (let i = 0; i < outputLength; i += columns) {
10151023
// The last lines may contain less entries than columns.
10161024
const max = Math.min(i + columns, outputLength);
1017-
for (var j = i + 1; j < max; j++) {
1018-
colorPadding = output[j].length - dataLen[j];
1019-
str += `, ${output[j].padStart(maxLength + colorPadding, ' ')}`;
1025+
let str = '';
1026+
let j = i;
1027+
for (; j < max - 1; j++) {
1028+
// Calculate extra color padding in case it's active. This has to be
1029+
// done line by line as some lines might contain more colors than
1030+
// others.
1031+
const padding = maxLineLength[j - i] + output[j].length - dataLen[j];
1032+
str += `${output[j]}, `[order](padding, ' ');
1033+
}
1034+
if (order === 'padStart') {
1035+
const padding = maxLineLength[j - i] +
1036+
output[j].length -
1037+
dataLen[j] -
1038+
separatorSpace;
1039+
str += output[j].padStart(padding, ' ');
1040+
} else {
1041+
str += output[j];
10201042
}
10211043
tmp.push(str);
10221044
}
@@ -1419,7 +1441,7 @@ function isBelowBreakLength(ctx, output, start, base) {
14191441
}
14201442

14211443
function reduceToSingleString(
1422-
ctx, output, base, braces, extrasType, recurseTimes) {
1444+
ctx, output, base, braces, extrasType, recurseTimes, value) {
14231445
if (ctx.compact !== true) {
14241446
if (typeof ctx.compact === 'number' && ctx.compact >= 1) {
14251447
// Memorize the original output length. In case the the output is grouped,
@@ -1428,7 +1450,7 @@ function reduceToSingleString(
14281450
// Group array elements together if the array contains at least six
14291451
// separate entries.
14301452
if (extrasType === kArrayExtrasType && entries > 6) {
1431-
output = groupArrayElements(ctx, output);
1453+
output = groupArrayElements(ctx, output, value);
14321454
}
14331455
// `ctx.currentDepth` is set to the most inner depth of the currently
14341456
// inspected object part while `recurseTimes` is the actual current depth

test/parallel/test-util-inspect.js

Lines changed: 86 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,11 +1306,11 @@ if (typeof Symbol !== 'undefined') {
13061306
{
13071307
const x = new Uint8Array(101);
13081308
assert(util.inspect(x).endsWith('1 more item\n]'));
1309-
assert(!util.inspect(x, { maxArrayLength: 101 }).endsWith('1 more item\n]'));
1309+
assert(!util.inspect(x, { maxArrayLength: 101 }).includes('1 more item'));
13101310
assert.strictEqual(util.inspect(x, { maxArrayLength: 0 }),
13111311
'Uint8Array [ ... 101 more items ]');
1312-
assert(!util.inspect(x, { maxArrayLength: null }).endsWith('1 more item\n]'));
1313-
assert(util.inspect(x, { maxArrayLength: Infinity }).endsWith(' 0, 0\n]'));
1312+
assert(!util.inspect(x, { maxArrayLength: null }).includes('1 more item'));
1313+
assert(util.inspect(x, { maxArrayLength: Infinity }).endsWith(' 0, 0\n]'));
13141314
}
13151315

13161316
{
@@ -1940,7 +1940,7 @@ assert.strictEqual(util.inspect('"\'${a}'), "'\"\\'${a}'");
19401940
[WeakMap, [[[{}, {}]]], '{ <items unknown> }'],
19411941
[BigInt64Array,
19421942
[10],
1943-
'[\n 0n, 0n, 0n,\n 0n, 0n, 0n,\n 0n, 0n, 0n,\n 0n\n]'],
1943+
'[\n 0n, 0n, 0n, 0n, 0n,\n 0n, 0n, 0n, 0n, 0n\n]'],
19441944
[Date, ['Sun, 14 Feb 2010 11:48:40 GMT'], '2010-02-14T11:48:40.000Z'],
19451945
[Date, ['invalid_date'], 'Invalid Date']
19461946
].forEach(([base, input, rawExpected]) => {
@@ -2180,21 +2180,18 @@ assert.strictEqual(
21802180
' b: [ 1, 2, [ 1, 2, { a: 1, b: 2, c: 3 } ] ],',
21812181
" c: [ 'foo', 4, 444444 ],",
21822182
' d: [',
2183-
' 0, 1, 4, 3, 16, 5, 36,',
2184-
' 7, 64, 9, 100, 11, 144, 13,',
2185-
' 196, 15, 256, 17, 324, 19, 400,',
2186-
' 21, 484, 23, 576, 25, 676, 27,',
2187-
' 784, 29, 900, 31, 1024, 33, 1156,',
2188-
' 35, 1296, 37, 1444, 39, 1600, 41,',
2189-
' 1764, 43, 1936, 45, 2116, 47, 2304,',
2190-
' 49, 2500, 51, 2704, 53, 2916, 55,',
2191-
' 3136, 57, 3364, 59, 3600, 61, 3844,',
2192-
' 63, 4096, 65, 4356, 67, 4624, 69,',
2193-
' 4900, 71, 5184, 73, 5476, 75, 5776,',
2194-
' 77, 6084, 79, 6400, 81, 6724, 83,',
2195-
' 7056, 85, 7396, 87, 7744, 89, 8100,',
2196-
' 91, 8464, 93, 8836, 95, 9216, 97,',
2197-
' 9604, 99,',
2183+
' 0, 1, 4, 3, 16, 5, 36, 7, 64,',
2184+
' 9, 100, 11, 144, 13, 196, 15, 256, 17,',
2185+
' 324, 19, 400, 21, 484, 23, 576, 25, 676,',
2186+
' 27, 784, 29, 900, 31, 1024, 33, 1156, 35,',
2187+
' 1296, 37, 1444, 39, 1600, 41, 1764, 43, 1936,',
2188+
' 45, 2116, 47, 2304, 49, 2500, 51, 2704, 53,',
2189+
' 2916, 55, 3136, 57, 3364, 59, 3600, 61, 3844,',
2190+
' 63, 4096, 65, 4356, 67, 4624, 69, 4900, 71,',
2191+
' 5184, 73, 5476, 75, 5776, 77, 6084, 79, 6400,',
2192+
' 81, 6724, 83, 7056, 85, 7396, 87, 7744, 89,',
2193+
' 8100, 91, 8464, 93, 8836, 95, 9216, 97, 9604,',
2194+
' 99,',
21982195
' ... 1 more item',
21992196
' ],',
22002197
' e: [',
@@ -2226,10 +2223,8 @@ assert.strictEqual(
22262223
" 'foobar baz'",
22272224
' ],',
22282225
' h: [',
2229-
' 100, 0, 1,',
2230-
' 2, 3, 4,',
2231-
' 5, 6, 7,',
2232-
' 8',
2226+
' 100, 0, 1, 2, 3,',
2227+
' 4, 5, 6, 7, 8',
22332228
' ],',
22342229
' long: [',
22352230
" 'This text is too long for grouping!',",
@@ -2257,15 +2252,15 @@ assert.strictEqual(
22572252

22582253
expected = [
22592254
'[',
2260-
' 1, 1, 1,',
2261-
' 1, 1, 1,',
2262-
' 1, 1, 1,',
2263-
' 1, 1, 1,',
2264-
' 1, 1, 1,',
2265-
' 1, 1, 1,',
2266-
' 1, 1, 1,',
2267-
' 1, 1, 1,',
2268-
' 1, 1, 123456789',
2255+
' 1, 1, 1,',
2256+
' 1, 1, 1,',
2257+
' 1, 1, 1,',
2258+
' 1, 1, 1,',
2259+
' 1, 1, 1,',
2260+
' 1, 1, 1,',
2261+
' 1, 1, 1,',
2262+
' 1, 1, 1,',
2263+
' 1, 1, 123456789',
22692264
']'
22702265
].join('\n');
22712266

@@ -2295,10 +2290,10 @@ assert.strictEqual(
22952290
' b: { x: \u001b[33m5\u001b[39m, c: \u001b[36m[Object]\u001b[39m }',
22962291
' },',
22972292
' b: [',
2298-
" \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2299-
" \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2300-
" \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2301-
" \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2293+
" \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2294+
" \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2295+
" \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
2296+
" \u001b[32m'foobar'\u001b[39m, \u001b[32m'baz'\u001b[39m,",
23022297
" \u001b[32m'foobar'\u001b[39m",
23032298
' ]',
23042299
'}',
@@ -2311,26 +2306,23 @@ assert.strictEqual(
23112306

23122307
expected = [
23132308
'[',
2314-
' \u001b[33m0\u001b[39m, \u001b[33m1\u001b[39m, \u001b[33m2\u001b[39m,',
2315-
' \u001b[33m3\u001b[39m, \u001b[33m4\u001b[39m, \u001b[33m5\u001b[39m,',
2316-
' \u001b[33m6\u001b[39m, \u001b[33m7\u001b[39m, \u001b[33m8\u001b[39m,',
2317-
' \u001b[33m9\u001b[39m, \u001b[33m10\u001b[39m, \u001b[33m11\u001b[39m,',
2318-
' \u001b[33m12\u001b[39m, \u001b[33m13\u001b[39m, \u001b[33m14\u001b[39m,',
2319-
' \u001b[33m15\u001b[39m, \u001b[33m16\u001b[39m, \u001b[33m17\u001b[39m,',
2320-
' \u001b[33m18\u001b[39m, \u001b[33m19\u001b[39m, \u001b[33m20\u001b[39m,',
2321-
' \u001b[33m21\u001b[39m, \u001b[33m22\u001b[39m, \u001b[33m23\u001b[39m,',
2322-
' \u001b[33m24\u001b[39m, \u001b[33m25\u001b[39m, \u001b[33m26\u001b[39m,',
2323-
' \u001b[33m27\u001b[39m, \u001b[33m28\u001b[39m, \u001b[33m29\u001b[39m,',
2324-
' \u001b[33m30\u001b[39m, \u001b[33m31\u001b[39m, \u001b[33m32\u001b[39m,',
2325-
' \u001b[33m33\u001b[39m, \u001b[33m34\u001b[39m, \u001b[33m35\u001b[39m,',
2326-
' \u001b[33m36\u001b[39m, \u001b[33m37\u001b[39m, \u001b[33m38\u001b[39m,',
2327-
' \u001b[33m39\u001b[39m, \u001b[33m40\u001b[39m, \u001b[33m41\u001b[39m,',
2328-
' \u001b[33m42\u001b[39m, \u001b[33m43\u001b[39m, \u001b[33m44\u001b[39m,',
2329-
' \u001b[33m45\u001b[39m, \u001b[33m46\u001b[39m, \u001b[33m47\u001b[39m,',
2330-
' \u001b[33m48\u001b[39m, \u001b[33m49\u001b[39m, \u001b[33m50\u001b[39m,',
2331-
' \u001b[33m51\u001b[39m, \u001b[33m52\u001b[39m, \u001b[33m53\u001b[39m,',
2332-
' \u001b[33m54\u001b[39m, \u001b[33m55\u001b[39m, \u001b[33m56\u001b[39m,',
2333-
' \u001b[33m57\u001b[39m, \u001b[33m58\u001b[39m, \u001b[33m59\u001b[39m',
2309+
/* eslint-disable max-len */
2310+
' \u001b[33m0\u001b[39m, \u001b[33m1\u001b[39m, \u001b[33m2\u001b[39m, \u001b[33m3\u001b[39m,',
2311+
' \u001b[33m4\u001b[39m, \u001b[33m5\u001b[39m, \u001b[33m6\u001b[39m, \u001b[33m7\u001b[39m,',
2312+
' \u001b[33m8\u001b[39m, \u001b[33m9\u001b[39m, \u001b[33m10\u001b[39m, \u001b[33m11\u001b[39m,',
2313+
' \u001b[33m12\u001b[39m, \u001b[33m13\u001b[39m, \u001b[33m14\u001b[39m, \u001b[33m15\u001b[39m,',
2314+
' \u001b[33m16\u001b[39m, \u001b[33m17\u001b[39m, \u001b[33m18\u001b[39m, \u001b[33m19\u001b[39m,',
2315+
' \u001b[33m20\u001b[39m, \u001b[33m21\u001b[39m, \u001b[33m22\u001b[39m, \u001b[33m23\u001b[39m,',
2316+
' \u001b[33m24\u001b[39m, \u001b[33m25\u001b[39m, \u001b[33m26\u001b[39m, \u001b[33m27\u001b[39m,',
2317+
' \u001b[33m28\u001b[39m, \u001b[33m29\u001b[39m, \u001b[33m30\u001b[39m, \u001b[33m31\u001b[39m,',
2318+
' \u001b[33m32\u001b[39m, \u001b[33m33\u001b[39m, \u001b[33m34\u001b[39m, \u001b[33m35\u001b[39m,',
2319+
' \u001b[33m36\u001b[39m, \u001b[33m37\u001b[39m, \u001b[33m38\u001b[39m, \u001b[33m39\u001b[39m,',
2320+
' \u001b[33m40\u001b[39m, \u001b[33m41\u001b[39m, \u001b[33m42\u001b[39m, \u001b[33m43\u001b[39m,',
2321+
' \u001b[33m44\u001b[39m, \u001b[33m45\u001b[39m, \u001b[33m46\u001b[39m, \u001b[33m47\u001b[39m,',
2322+
' \u001b[33m48\u001b[39m, \u001b[33m49\u001b[39m, \u001b[33m50\u001b[39m, \u001b[33m51\u001b[39m,',
2323+
' \u001b[33m52\u001b[39m, \u001b[33m53\u001b[39m, \u001b[33m54\u001b[39m, \u001b[33m55\u001b[39m,',
2324+
' \u001b[33m56\u001b[39m, \u001b[33m57\u001b[39m, \u001b[33m58\u001b[39m, \u001b[33m59\u001b[39m',
2325+
/* eslint-enable max-len */
23342326
']'
23352327
].join('\n');
23362328

@@ -2389,44 +2381,44 @@ assert.strictEqual(
23892381
);
23902382
expected = [
23912383
'[',
2392-
" 'Object', 'Function', 'Array',",
2393-
" 'Number', 'parseFloat', 'parseInt',",
2394-
" 'Infinity', 'NaN', 'undefined',",
2395-
" 'Boolean', 'String', 'Symbol',",
2396-
" 'Date', 'Promise', 'RegExp',",
2397-
" 'Error', 'EvalError', 'RangeError',",
2398-
" 'ReferenceError', 'SyntaxError', 'TypeError',",
2399-
" 'URIError', 'JSON', 'Math',",
2400-
" 'console', 'Intl', 'ArrayBuffer',",
2401-
" 'Uint8Array', 'Int8Array', 'Uint16Array',",
2402-
" 'Int16Array', 'Uint32Array', 'Int32Array',",
2403-
" 'Float32Array', 'Float64Array', 'Uint8ClampedArray',",
2404-
" 'BigUint64Array', 'BigInt64Array', 'DataView',",
2405-
" 'Map', 'BigInt', 'Set',",
2406-
" 'WeakMap', 'WeakSet', 'Proxy',",
2407-
" 'Reflect', 'decodeURI', 'decodeURIComponent',",
2408-
" 'encodeURI', 'encodeURIComponent', 'escape',",
2409-
" 'unescape', 'eval', 'isFinite',",
2410-
" 'isNaN', 'SharedArrayBuffer', 'Atomics',",
2411-
" 'globalThis', 'WebAssembly', 'global',",
2412-
" 'process', 'GLOBAL', 'root',",
2413-
" 'Buffer', 'URL', 'URLSearchParams',",
2414-
" 'TextEncoder', 'TextDecoder', 'clearInterval',",
2415-
" 'clearTimeout', 'setInterval', 'setTimeout',",
2416-
" 'queueMicrotask', 'clearImmediate', 'setImmediate',",
2417-
" 'module', 'require', 'assert',",
2418-
" 'async_hooks', 'buffer', 'child_process',",
2419-
" 'cluster', 'crypto', 'dgram',",
2420-
" 'dns', 'domain', 'events',",
2421-
" 'fs', 'http', 'http2',",
2422-
" 'https', 'inspector', 'net',",
2423-
" 'os', 'path', 'perf_hooks',",
2424-
" 'punycode', 'querystring', 'readline',",
2425-
" 'repl', 'stream', 'string_decoder',",
2426-
" 'tls', 'trace_events', 'tty',",
2427-
" 'url', 'v8', 'vm',",
2428-
" 'worker_threads', 'zlib', '_',",
2429-
" '_error', 'util'",
2384+
" 'Object', 'Function', 'Array',",
2385+
" 'Number', 'parseFloat', 'parseInt',",
2386+
" 'Infinity', 'NaN', 'undefined',",
2387+
" 'Boolean', 'String', 'Symbol',",
2388+
" 'Date', 'Promise', 'RegExp',",
2389+
" 'Error', 'EvalError', 'RangeError',",
2390+
" 'ReferenceError', 'SyntaxError', 'TypeError',",
2391+
" 'URIError', 'JSON', 'Math',",
2392+
" 'console', 'Intl', 'ArrayBuffer',",
2393+
" 'Uint8Array', 'Int8Array', 'Uint16Array',",
2394+
" 'Int16Array', 'Uint32Array', 'Int32Array',",
2395+
" 'Float32Array', 'Float64Array', 'Uint8ClampedArray',",
2396+
" 'BigUint64Array', 'BigInt64Array', 'DataView',",
2397+
" 'Map', 'BigInt', 'Set',",
2398+
" 'WeakMap', 'WeakSet', 'Proxy',",
2399+
" 'Reflect', 'decodeURI', 'decodeURIComponent',",
2400+
" 'encodeURI', 'encodeURIComponent', 'escape',",
2401+
" 'unescape', 'eval', 'isFinite',",
2402+
" 'isNaN', 'SharedArrayBuffer', 'Atomics',",
2403+
" 'globalThis', 'WebAssembly', 'global',",
2404+
" 'process', 'GLOBAL', 'root',",
2405+
" 'Buffer', 'URL', 'URLSearchParams',",
2406+
" 'TextEncoder', 'TextDecoder', 'clearInterval',",
2407+
" 'clearTimeout', 'setInterval', 'setTimeout',",
2408+
" 'queueMicrotask', 'clearImmediate', 'setImmediate',",
2409+
" 'module', 'require', 'assert',",
2410+
" 'async_hooks', 'buffer', 'child_process',",
2411+
" 'cluster', 'crypto', 'dgram',",
2412+
" 'dns', 'domain', 'events',",
2413+
" 'fs', 'http', 'http2',",
2414+
" 'https', 'inspector', 'net',",
2415+
" 'os', 'path', 'perf_hooks',",
2416+
" 'punycode', 'querystring', 'readline',",
2417+
" 'repl', 'stream', 'string_decoder',",
2418+
" 'tls', 'trace_events', 'tty',",
2419+
" 'url', 'v8', 'vm',",
2420+
" 'worker_threads', 'zlib', '_',",
2421+
" '_error', 'util'",
24302422
']'
24312423
].join('\n');
24322424

0 commit comments

Comments
 (0)