diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 27e25dbf17f..7411d9c094a 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -953,8 +953,8 @@ function sanitizeData(data: string): string { // Strip escape sequences so winpty/conpty doesn't cause flakiness, do for all platforms for // consistency - const terminalCodesRegex = /(?:\u001B|\u009B)[\[\]()#;?]*(?:(?:(?:[a-zA-Z0-9]*(?:;[a-zA-Z0-9]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-PR-TZcf-ntqry=><~]))/g; - data = data.replace(terminalCodesRegex, ''); + const CSI_SEQUENCE = /(:?(:?\x1b\[|\x9B)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~])|(:?\x1b\].*?\x07)/g; + data = data.replace(CSI_SEQUENCE, ''); return data; } diff --git a/src/vs/base/common/ansi.ts b/src/vs/base/common/ansi.ts deleted file mode 100644 index 7a5d93bbfb8..00000000000 --- a/src/vs/base/common/ansi.ts +++ /dev/null @@ -1,35 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Lazy } from 'vs/base/common/lazy'; - -const ansiEscapeSequenceRegex = new Lazy(() => /(?:\u001B|\u009B)[\[\]()#;?]*(?:(?:(?:[a-zA-Z0-9]*(?:;[a-zA-Z0-9]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-PR-TZcf-ntqry=><~]))/g); -const promptNonPrintableCharactersRegex = new Lazy(() => /\\\[.*?\\\]/g); - -/** - * Strips ANSI escape sequences from a string. - * @param data The data to strip the ANSI escape sequences from. - * - * @example - * stripAnsiEscapeSequences('\u001b[31mHello, World!\u001b[0m'); - * // 'Hello, World!' - */ -export function stripAnsiEscapeSequences(data: string): string { - return data.replace(ansiEscapeSequenceRegex.value, ''); -} - -/** - * Strips ANSI escape sequences from a UNIX-style prompt string (eg. `$PS1`). - * @param data The data to strip the ANSI escape sequences from. - * - * @example - * stripAnsiEscapeSequencesFromPrompt('\n\\[\u001b[01;34m\\]\\w\\[\u001b[00m\\]\n\\[\u001b[1;32m\\]> \\[\u001b[0m\\]'); - * // '\n\\w\n> ' - */ -export function stripAnsiEscapeSequencesFromPrompt(data: string): string { - return (data - .replace(ansiEscapeSequenceRegex.value, '') - .replace(promptNonPrintableCharactersRegex.value, '')); -} diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index be372198ee7..51f74227383 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -786,6 +786,14 @@ export function* forAnsiStringParts(str: string) { } } +/** + * Strips ANSI escape sequences from a string. + * @param str The dastringa stringo strip the ANSI escape sequences from. + * + * @example + * removeAnsiEscapeCodes('\u001b[31mHello, World!\u001b[0m'); + * // 'Hello, World!' + */ export function removeAnsiEscapeCodes(str: string): string { if (str) { str = str.replace(CSI_SEQUENCE, ''); @@ -794,6 +802,21 @@ export function removeAnsiEscapeCodes(str: string): string { return str; } +const PROMPT_NON_PRINTABLE = /\\\[.*?\\\]/g; + +/** + * Strips ANSI escape sequences from a UNIX-style prompt string (eg. `$PS1`). + * @param str The string to strip the ANSI escape sequences from. + * + * @example + * removeAnsiEscapeCodesFromPrompt('\n\\[\u001b[01;34m\\]\\w\\[\u001b[00m\\]\n\\[\u001b[1;32m\\]> \\[\u001b[0m\\]'); + * // '\n\\w\n> ' + */ +export function removeAnsiEscapeCodesFromPrompt(str: string): string { + return removeAnsiEscapeCodes(str).replace(PROMPT_NON_PRINTABLE, ''); +} + + // -- UTF-8 BOM export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM); diff --git a/src/vs/base/test/common/ansi.test.ts b/src/vs/base/test/common/ansi.test.ts deleted file mode 100644 index 0bdf32840cf..00000000000 --- a/src/vs/base/test/common/ansi.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { strictEqual } from 'assert'; -import { stripAnsiEscapeSequences, stripAnsiEscapeSequencesFromPrompt } from 'vs/base/common/ansi'; -import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; - -suite('ansi', () => { - ensureNoDisposablesAreLeakedInTestSuite(); - - suite('stripAnsiEscapeSequences', () => { - test('should strip simple SGR escape sequences', () => { - strictEqual(stripAnsiEscapeSequences('\u001b[31mHello, World!\u001b[0m'), 'Hello, World!'); - }); - test('should strip complex SGR escape sequences', () => { - strictEqual(stripAnsiEscapeSequences('\u001b[38;2;255;82;197;48;2;155;106;0mHello, World!\u001b[0m'), 'Hello, World!'); - }); - test('should strip ED, EL escape sequences', () => { - strictEqual(stripAnsiEscapeSequences('\u001b[KHello, World!\r\n\u001b[2J'), 'Hello, World!\r\n'); - }); - }); - - suite('stripAnsiEscapeSequencesFromPrompt', () => { - test('should strip simple SGR escape sequences', () => { - strictEqual(stripAnsiEscapeSequences('\u001b[31m$ \u001b[0m'), '$ '); - }); - test('should strip \[ and \] chars and their contents', () => { - strictEqual(stripAnsiEscapeSequencesFromPrompt('\n\\[\u001b[01;34m\\]\\w\\[\u001b[00m\\]\n\\[\u001b[1;32m\\]> \\[\u001b[0m\\]'), '\n\\w\n> '); - }); - }); -}); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 73c04ad7239..655bc2b4255 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -538,6 +538,11 @@ suite('Strings', () => { } }); + test('removeAnsiEscapeCodesFromPrompt', () => { + assert.strictEqual(strings.removeAnsiEscapeCodesFromPrompt('\u001b[31m$ \u001b[0m'), '$ '); + assert.strictEqual(strings.removeAnsiEscapeCodesFromPrompt('\n\\[\u001b[01;34m\\]\\w\\[\u001b[00m\\]\n\\[\u001b[1;32m\\]> \\[\u001b[0m\\]'), '\n\\w\n> '); + }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 3404e18e528..de493bd36ab 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -21,7 +21,7 @@ import { BufferMarkCapability } from 'vs/platform/terminal/common/capabilities/b import type { ITerminalAddon, Terminal } from '@xterm/headless'; import { URI } from 'vs/base/common/uri'; import { sanitizeCwd } from 'vs/platform/terminal/common/terminalEnvironment'; -import { stripAnsiEscapeSequencesFromPrompt } from 'vs/base/common/ansi'; +import { removeAnsiEscapeCodesFromPrompt } from 'vs/base/common/strings'; /** @@ -383,7 +383,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } switch (key) { case 'ContinuationPrompt': { - this._updateContinuationPrompt(stripAnsiEscapeSequencesFromPrompt(value)); + this._updateContinuationPrompt(removeAnsiEscapeCodesFromPrompt(value)); return true; } case 'Cwd': { diff --git a/src/vs/workbench/contrib/remote/browser/urlFinder.ts b/src/vs/workbench/contrib/remote/browser/urlFinder.ts index db63ef81eff..e2995bb5f2d 100644 --- a/src/vs/workbench/contrib/remote/browser/urlFinder.ts +++ b/src/vs/workbench/contrib/remote/browser/urlFinder.ts @@ -7,7 +7,7 @@ import { ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/termin import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IDebugService, IDebugSession, IReplElement } from 'vs/workbench/contrib/debug/common/debug'; -import { stripAnsiEscapeSequences } from 'vs/base/common/ansi'; +import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; export class UrlFinder extends Disposable { /** @@ -99,7 +99,7 @@ export class UrlFinder extends Disposable { private processData(data: string) { // strip ANSI terminal codes - data = stripAnsiEscapeSequences(data); + data = removeAnsiEscapeCodes(data); const urlMatches = data.match(UrlFinder.localUrlRegex) || []; if (urlMatches && urlMatches.length > 0) { urlMatches.forEach((match) => {