diff --git a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts index 328f5f941ef..a9a3f6a90d7 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts @@ -113,6 +113,7 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { onCommandStart: Event, onCommandStartChanged: Event, onCommandExecuted: Event, + onCommandFinished: Event, @ILogService private readonly _logService: ILogService ) { super(); @@ -127,6 +128,7 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { this._register(onCommandStart(e => this._handleCommandStart(e as { marker: IMarker }))); this._register(onCommandStartChanged(() => this._handleCommandStartChanged())); this._register(onCommandExecuted(() => this._handleCommandExecuted())); + this._register(onCommandFinished(() => this._handleCommandFinished())); this._register(this.onDidStartInput(() => this._logCombinedStringIfTrace('PromptInputModel#onDidStartInput'))); this._register(this.onDidChangeInput(() => this._logCombinedStringIfTrace('PromptInputModel#onDidChangeInput'))); @@ -261,6 +263,13 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { this._onDidChangeInput.fire(event); } + private _handleCommandFinished() { + // Clear the prompt input value when command finishes to prepare for the next command + // This prevents runCommand from detecting leftover text and sending ^C unnecessarily + this._value = ''; + this._onDidChangeInput.fire(this._createStateObject()); + } + @throttle(0) private _sync() { try { diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index e52d286d20e..259fc6ef00c 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -84,7 +84,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe ) { super(); this._currentCommand = new PartialTerminalCommand(this._terminal); - this._promptInputModel = this._register(new PromptInputModel(this._terminal, this.onCommandStarted, this.onCommandStartChanged, this.onCommandExecuted, this._logService)); + this._promptInputModel = this._register(new PromptInputModel(this._terminal, this.onCommandStarted, this.onCommandStartChanged, this.onCommandExecuted, this.onCommandFinished, this._logService)); // Pull command line from the buffer if it was not set explicitly this._register(this.onCommandExecuted(command => { diff --git a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts index 64fe94b7ab9..e625ae66a9b 100644 --- a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts +++ b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts @@ -24,6 +24,7 @@ suite('PromptInputModel', () => { let onCommandStart: Emitter; let onCommandStartChanged: Emitter; let onCommandExecuted: Emitter; + let onCommandFinished: Emitter; async function writePromise(data: string) { await new Promise(r => xterm.write(data, r)); @@ -37,6 +38,10 @@ suite('PromptInputModel', () => { onCommandExecuted.fire(null!); } + function fireCommandFinished() { + onCommandFinished.fire(null!); + } + function setContinuationPrompt(prompt: string) { promptInputModel.setContinuationPrompt(prompt); } @@ -68,7 +73,8 @@ suite('PromptInputModel', () => { onCommandStart = store.add(new Emitter()); onCommandStartChanged = store.add(new Emitter()); onCommandExecuted = store.add(new Emitter()); - promptInputModel = store.add(new PromptInputModel(xterm, onCommandStart.event, onCommandStartChanged.event, onCommandExecuted.event, new NullLogService)); + onCommandFinished = store.add(new Emitter()); + promptInputModel = store.add(new PromptInputModel(xterm, onCommandStart.event, onCommandStartChanged.event, onCommandExecuted.event, onCommandFinished.event, new NullLogService)); }); test('basic input and execute', async () => { @@ -138,6 +144,21 @@ suite('PromptInputModel', () => { }); }); + test('should clear value when command finishes', async () => { + await writePromise('$ '); + fireCommandStart(); + await assertPromptInput('|'); + + await writePromise('echo hello'); + await assertPromptInput('echo hello|'); + + fireCommandExecuted(); + strictEqual(promptInputModel.value, 'echo hello'); + + fireCommandFinished(); + strictEqual(promptInputModel.value, ''); + }); + test('cursor navigation', async () => { await writePromise('$ '); fireCommandStart(); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/recordings/rich/macos_zsh_omz_echo_3_times.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/recordings/rich/macos_zsh_omz_echo_3_times.ts index 984e205d9a5..fb03eb65f07 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/recordings/rich/macos_zsh_omz_echo_3_times.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/recordings/rich/macos_zsh_omz_echo_3_times.ts @@ -137,10 +137,6 @@ export const events = [ "type": "output", "data": "" }, - { - "type": "promptInputChange", - "data": "echo a" - }, { "type": "output", "data": "\u001b]633;P;Cwd=/Users/tyriar/playground/test1\u0007\u001b]633;EnvSingleStart;0;448d50d0-70fe-4ab5-842e-132f3b1c159a;\u0007\u001b]633;EnvSingleEnd;448d50d0-70fe-4ab5-842e-132f3b1c159a;\u0007\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b]633;A\u0007tyriar@Mac test1 % \u001b]633;B\u0007\u001b[K\u001b[?2004h" @@ -245,10 +241,6 @@ export const events = [ "type": "output", "data": "" }, - { - "type": "promptInputChange", - "data": "echo b" - }, { "type": "output", "data": "\u001b]633;P;Cwd=/Users/tyriar/playground/test1\u0007\u001b]633;EnvSingleStart;0;448d50d0-70fe-4ab5-842e-132f3b1c159a;\u0007\u001b]633;EnvSingleEnd;448d50d0-70fe-4ab5-842e-132f3b1c159a;\u0007\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b]633;A\u0007tyriar@Mac test1 % \u001b]633;B\u0007\u001b[K\u001b[?2004h" @@ -357,10 +349,6 @@ export const events = [ "type": "output", "data": "" }, - { - "type": "promptInputChange", - "data": "echo c" - }, { "type": "output", "data": "\u001b]633;P;Cwd=/Users/tyriar/playground/test1\u0007\u001b]633;EnvSingleStart;0;448d50d0-70fe-4ab5-842e-132f3b1c159a;\u0007\u001b]633;EnvSingleEnd;448d50d0-70fe-4ab5-842e-132f3b1c159a;\u0007\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b]633;A\u0007tyriar@Mac test1 % \u001b]633;B\u0007\u001b[K\u001b[?2004h"