diff --git a/package-lock.json b/package-lock.json index 1f484e416cbf5..b1ae7c40d8015 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "native-is-elevated": "0.8.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta40", + "node-pty": "^1.1.0-beta42", "open": "^10.1.2", "tas-client": "0.3.1", "undici": "^7.9.0", @@ -12809,9 +12809,9 @@ } }, "node_modules/node-pty": { - "version": "1.1.0-beta40", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta40.tgz", - "integrity": "sha512-ACjAwX4Fb6jApK082jXKJqpeguZq5uTgcM4bRurJ7uxaPX9mE4F4yTHm8gEbn6nLSvEmF4EiBCxr6t/HHH+Dgg==", + "version": "1.1.0-beta42", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta42.tgz", + "integrity": "sha512-59KoV6xxhJciRVpo4lQ9wnP38SPaBlXgwszYS8nlHAHrt02d14peg+kHtJ4AOtyLWiCf8WPCeJNbxBkiA7Oy7Q==", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index b0fdae1627bd1..9aa3be3e55b66 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "native-is-elevated": "0.8.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta40", + "node-pty": "^1.1.0-beta42", "open": "^10.1.2", "tas-client": "0.3.1", "undici": "^7.9.0", diff --git a/remote/package-lock.json b/remote/package-lock.json index 55d0715a4e24b..95ef4ffb52625 100644 --- a/remote/package-lock.json +++ b/remote/package-lock.json @@ -38,7 +38,7 @@ "kerberos": "2.1.1", "minimist": "^1.2.8", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta40", + "node-pty": "^1.1.0-beta42", "tas-client": "0.3.1", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", @@ -848,9 +848,9 @@ } }, "node_modules/node-pty": { - "version": "1.1.0-beta40", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta40.tgz", - "integrity": "sha512-ACjAwX4Fb6jApK082jXKJqpeguZq5uTgcM4bRurJ7uxaPX9mE4F4yTHm8gEbn6nLSvEmF4EiBCxr6t/HHH+Dgg==", + "version": "1.1.0-beta42", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta42.tgz", + "integrity": "sha512-59KoV6xxhJciRVpo4lQ9wnP38SPaBlXgwszYS8nlHAHrt02d14peg+kHtJ4AOtyLWiCf8WPCeJNbxBkiA7Oy7Q==", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/remote/package.json b/remote/package.json index e4754d9ed54d9..1ef9407ef290e 100644 --- a/remote/package.json +++ b/remote/package.json @@ -33,7 +33,7 @@ "kerberos": "2.1.1", "minimist": "^1.2.8", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta40", + "node-pty": "^1.1.0-beta42", "tas-client": "0.3.1", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", diff --git a/src/vs/platform/terminal/common/terminalProcess.ts b/src/vs/platform/terminal/common/terminalProcess.ts index 917db9710d872..28408eef7103b 100644 --- a/src/vs/platform/terminal/common/terminalProcess.ts +++ b/src/vs/platform/terminal/common/terminalProcess.ts @@ -69,41 +69,3 @@ export interface ReplayEntry { rows: number; data: string; } - -const enum Constants { - /** - * Writing large amounts of data can be corrupted for some reason, after looking into this is - * appears to be a race condition around writing to the FD which may be based on how powerful - * the hardware is. The workaround for this is to space out when large amounts of data is being - * written to the terminal. See https://github.com/microsoft/vscode/issues/38137 - */ - WriteMaxChunkSize = 50, -} - -/** - * Splits incoming pty data into chunks to try prevent data corruption that could occur when pasting - * large amounts of data. - */ -export function chunkInput(data: string): string[] { - const chunks: string[] = []; - let nextChunkStartIndex = 0; - for (let i = 0; i < data.length - 1; i++) { - if ( - // If the max chunk size is reached - i - nextChunkStartIndex + 1 >= Constants.WriteMaxChunkSize || - // If the next character is ESC, send the pending data to avoid splitting the escape - // sequence. - data[i + 1] === '\x1b' - ) { - chunks.push(data.substring(nextChunkStartIndex, i + 1)); - nextChunkStartIndex = i + 1; - // Skip the next character as the chunk would be a single character - i++; - } - } - // Push final chunk - if (nextChunkStartIndex !== data.length) { - chunks.push(data.substring(nextChunkStartIndex)); - } - return chunks; -} diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index e6deacd6b4dd6..03a6f35b42824 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -20,7 +20,6 @@ import { ChildProcessMonitor } from './childProcessMonitor.js'; import { getShellIntegrationInjection, getWindowsBuildNumber, IShellIntegrationConfigInjection } from './terminalEnvironment.js'; import { WindowsShellHelper } from './windowsShellHelper.js'; import { IPty, IPtyForkOptions, IWindowsPtyForkOptions, spawn } from 'node-pty'; -import { chunkInput } from '../common/terminalProcess.js'; import { isNumber } from '../../../base/common/types.js'; const enum ShutdownConstants { @@ -57,15 +56,6 @@ const enum Constants { * interval. */ KillSpawnSpacingDuration = 50, - /** - * How long to wait between chunk writes. - */ - WriteInterval = 5, -} - -interface IWriteObject { - data: string; - isBinary: boolean; } const posixShellTypeMap = new Map([ @@ -113,8 +103,6 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess private _windowsShellHelper: WindowsShellHelper | undefined; private _childProcessMonitor: ChildProcessMonitor | undefined; private _titleInterval: Timeout | undefined; - private _writeQueue: IWriteObject[] = []; - private _writeTimeout: Timeout | undefined; private _delayedResizer: DelayedResizer | undefined; private readonly _initialCwd: string; private readonly _ptyOptions: IPtyForkOptions | IWindowsPtyForkOptions; @@ -471,13 +459,13 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } input(data: string, isBinary: boolean = false): void { - if (this._store.isDisposed || !this._ptyProcess) { - return; + this._logService.trace('node-pty.IPty#write', data, isBinary); + if (isBinary) { + this._ptyProcess!.write(Buffer.from(data, 'binary')); + } else { + this._ptyProcess!.write(data); } - this._writeQueue.push(...chunkInput(data).map(e => { - return { isBinary, data: e }; - })); - this._startWrite(); + this._childProcessMonitor?.handleInput(); } sendSignal(signal: string): void { @@ -522,40 +510,6 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } } - private _startWrite(): void { - // Don't write if it's already queued of is there is nothing to write - if (this._writeTimeout !== undefined || this._writeQueue.length === 0) { - return; - } - - this._doWrite(); - - // Don't queue more writes if the queue is empty - if (this._writeQueue.length === 0) { - this._writeTimeout = undefined; - return; - } - - // Queue the next write - this._writeTimeout = setTimeout(() => { - this._writeTimeout = undefined; - this._startWrite(); - }, Constants.WriteInterval); - } - - private _doWrite(): void { - const object = this._writeQueue.shift()!; - this._logService.trace('node-pty.IPty#write', object.data); - if (object.isBinary) { - // TODO: node-pty's write should accept a Buffer, needs https://github.com/microsoft/node-pty/pull/812 - // eslint-disable-next-line local/code-no-any-casts, @typescript-eslint/no-explicit-any - this._ptyProcess!.write(Buffer.from(object.data, 'binary') as any); - } else { - this._ptyProcess!.write(object.data); - } - this._childProcessMonitor?.handleInput(); - } - resize(cols: number, rows: number): void { if (this._store.isDisposed) { return; diff --git a/src/vs/platform/terminal/test/common/terminalProcess.test.ts b/src/vs/platform/terminal/test/common/terminalProcess.test.ts deleted file mode 100644 index 4d4ef97709c9f..0000000000000 --- a/src/vs/platform/terminal/test/common/terminalProcess.test.ts +++ /dev/null @@ -1,41 +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 { deepStrictEqual } from 'assert'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { chunkInput } from '../../common/terminalProcess.js'; - -suite('platform - terminalProcess', () => { - ensureNoDisposablesAreLeakedInTestSuite(); - suite('chunkInput', () => { - test('single chunk', () => { - deepStrictEqual(chunkInput('foo bar'), ['foo bar']); - }); - test('multi chunk', () => { - deepStrictEqual(chunkInput('foo'.repeat(50)), [ - 'foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofo', - 'ofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoof', - 'oofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo' - ]); - }); - test('small data with escapes', () => { - deepStrictEqual(chunkInput('foo \x1b[30mbar'), [ - 'foo ', - '\x1b[30mbar' - ]); - }); - test('large data with escapes', () => { - deepStrictEqual(chunkInput('foofoofoofoo\x1b[30mbarbarbarbarbar\x1b[0m'.repeat(3)), [ - 'foofoofoofoo', - '\x1B[30mbarbarbarbarbar', - '\x1B[0mfoofoofoofoo', - '\x1B[30mbarbarbarbarbar', - '\x1B[0mfoofoofoofoo', - '\x1B[30mbarbarbarbarbar', - '\x1B[0m' - ]); - }); - }); -});