Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions lib/internal/util/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,24 @@ const meta = [
// License: MIT by Sindre Sorhus <sindresorhus@gmail.com>
// Matches all ansi escape code sequences in a string
const ansi = new RegExp(
// Sequences that run until a string terminator, such as OSC 8 hyperlinks.
'[\\u001B\\u009B][[\\]()#;?]*' +
'(?:(?:(?:(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]+)*' +
'(?:(?:(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]+)*' +
'|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/\\#&.:=?%@~_]*)*)?' +
'(?:\\u0007|\\u001B\\u005C|\\u009C))' +
'|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?' +
'[\\dA-PR-TZcf-nq-uy=><~]))', 'g',
// Control sequences, using the structure ECMA-48 5.4 defines: any number
// of parameter bytes (0x30-0x3F), any number of intermediate bytes
// (0x20-0x2F), then a single final byte (0x40-0x7E). The list of final
// bytes below covers neither parameter bytes such as `<`, used by mouse
// reports, nor final bytes such as `@`, so it stops in the middle of the
// sequence and leaves the rest in the output.
'|(?:\\u001B\\[|\\u009B)[[\\]()#]*' +
'[\\u0030-\\u003F]*[\\u0020-\\u002F]*[\\u0040-\\u007E]' +
// Everything else the reference implementation matched, including
// sequences whose last byte is a digit, such as `ESC 7`.
'|[\\u001B\\u009B][[\\]()#;?]*' +
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?' +
'[\\dA-PR-TZcf-nq-uy=><~])', 'g',
);

let getStringWidth;
Expand Down
24 changes: 24 additions & 0 deletions test/parallel/test-util-stripvtcontrolcharacters.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@ for (const ST of ['\u0007', '\u001B\u005C', '\u009C']) {
);
}

// Ref: ECMA-48 5.4. A control sequence is CSI, then any number of parameter
// bytes (0x30-0x3F), then any number of intermediate bytes (0x20-0x2F), then a
// single final byte (0x40-0x7E). Every sequence below is well formed and in use
// by terminals today, and every one of them used to have its tail left behind.
tests.push(
// Parameter bytes other than digits and `;`.
['a\u001B[<35;10;20Mb', 'ab'], // SGR mouse report
['a\u001B[>1;2cb', 'ab'], // Secondary device attributes
['a\u001B[=5hb', 'ab'],
['a\u009B<35;10;20Mb', 'ab'], // Same, with the 8 bit CSI introducer
// Final bytes that the reference implementation does not list.
['a\u001B[3@b', 'ab'], // ICH, insert character
['a\u001B[3Xb', 'ab'], // ECH, erase character
['a\u001B[5db', 'ab'], // VPA, line position absolute
['a\u001B[3bb', 'ab'], // REP, repeat preceding character
['a\u001B[2ab', 'ab'], // HPR, character position forward
// Sub parameters are separated by `:`, which is a parameter byte as well.
['a\u001B[38:2:255:0:0mb', 'ab'], // Truecolour in its colon form
['a\u001B[4:3mb', 'ab'], // Curly underline
// Intermediate bytes.
['a\u001B[2 qb', 'ab'], // DECSCUSR, set cursor style
['a\u001B[!pb', 'ab'], // DECSTR, soft terminal reset
);

test('util.stripVTControlCharacters', (t) => {
for (const [before, expected] of tests) {
t.assert.strictEqual(util.stripVTControlCharacters(before), expected);
Expand Down
Loading