diff --git a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts index 328f5f941ef82..28bb1cd86c230 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts @@ -42,6 +42,12 @@ export interface IPromptInputModel extends IPromptInputModelState { getCombinedString(emptyStringWhenEmpty?: boolean): string; setShellType(shellType?: TerminalShellType): void; + + /** + * Clears the prompt input value. This should be called when the command has finished + * and the value is no longer relevant as editable prompt input. + */ + clearValue(): void; } export interface IPromptInputModelState { @@ -239,8 +245,10 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { private _handleCommandExecuted() { if (this._state === PromptInputState.Execute) { + this._logService.trace('PromptInputModel#_handleCommandExecuted: already in Execute state'); return; } + this._logService.trace('PromptInputModel#_handleCommandExecuted: Inside _handleCommandExecuted but not PromptInputState.Execute yet'); this._cursorIndex = -1; @@ -261,6 +269,14 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { this._onDidChangeInput.fire(event); } + /** + * Clears the prompt input value. This should be called when the command has finished + * and the value is no longer relevant as editable prompt input. + */ + clearValue(): void { + this._value = ''; + } + @throttle(0) private _sync() { try { diff --git a/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts b/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts index 4a46691ae5682..c4c0fd07ed794 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts @@ -12,6 +12,7 @@ import { IWorkbenchEnvironmentService } from '../../services/environment/common/ import { extHostNamedCustomer, type IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { TerminalShellExecutionCommandLineConfidence } from '../common/extHostTypes.js'; import { IExtensionService } from '../../services/extensions/common/extensions.js'; +import { ITerminalLogService } from '../../../platform/terminal/common/terminal.js'; @extHostNamedCustomer(MainContext.MainThreadTerminalShellIntegration) export class MainThreadTerminalShellIntegration extends Disposable implements MainThreadTerminalShellIntegrationShape { @@ -21,7 +22,8 @@ export class MainThreadTerminalShellIntegration extends Disposable implements Ma extHostContext: IExtHostContext, @ITerminalService private readonly _terminalService: ITerminalService, @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService, - @IExtensionService private readonly _extensionService: IExtensionService + @IExtensionService private readonly _extensionService: IExtensionService, + @ITerminalLogService private readonly _logService: ITerminalLogService ) { super(); @@ -98,10 +100,15 @@ export class MainThreadTerminalShellIntegration extends Disposable implements Ma currentCommand = undefined; const instanceId = e.instance.instanceId; instanceDataListeners.get(instanceId)?.dispose(); + // Clear the prompt input value before sending the event to the extension host + // so that subsequent executeCommand calls don't see stale input + // e.instance.capabilities.get(TerminalCapability.CommandDetection)?.promptInputModel.clearValue(); + this._logService.trace(`PromptInputModel#_onDidEndTerminalShellExecution# state of promptInputModel is : ${e.instance.capabilities.get(TerminalCapability.CommandDetection)?.promptInputModel.state}`); // Shell integration C (executed) and D (command finished) sequences should always be in // their own events, so send this immediately. This means that the D sequence will not // be included as it's currently being parsed when the command finished event fires. this._proxy.$shellExecutionEnd(instanceId, e.data.command, convertToExtHostCommandLineConfidence(e.data), e.data.isTrusted, e.data.exitCode); + this._logService.trace(`PromptInputModel#_onDidEndTerminalShellExecution# state of promptInputModel after shellExecutionEnd is : ${e.instance.capabilities.get(TerminalCapability.CommandDetection)?.promptInputModel.state}`); })); // Clean up after dispose diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index ce934e637ac01..454c45124696d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1008,6 +1008,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Determine whether to send ETX (ctrl+c) before running the command. Only do this when the // command will be executed immediately or when command detection shows the prompt contains text. if (shouldExecute && (!commandDetection || commandDetection.promptInputModel.value.length > 0)) { + this._logService.trace(`PromptInputModel#runCommand: Sending ETX (ctrl+c) before running command`); + this._logService.trace(`PromptInputModel#runCommand: state of promptinputmodel before sending ETX: ${commandDetection ? commandDetection.promptInputModel.state : 'no command detection'}`); + this._logService.trace(`PromptInputModel#runCommand: value of promptinputmodel before sending ETX: ${commandDetection ? commandDetection.promptInputModel.value : 'no command detection'}`); + await this.sendText('\x03', false); // Wait a little before running the command to avoid the sequences being echoed while the ^C // is being evaluated