Skip to content

Commit 46a3b91

Browse files
committed
execute REPL command with queue!
1 parent 5e0035a commit 46a3b91

File tree

1 file changed

+87
-6
lines changed

1 file changed

+87
-6
lines changed

src/client/common/terminal/service.ts

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { IServiceContainer } from '../../ioc/types';
99
import { captureTelemetry } from '../../telemetry';
1010
import { EventName } from '../../telemetry/constants';
1111
import { ITerminalAutoActivation } from '../../terminals/types';
12-
import { ITerminalManager } from '../application/types';
12+
import { IApplicationShell, ITerminalManager } from '../application/types';
1313
import { _SCRIPTS_DIR } from '../process/internal/scripts/constants';
1414
import { IConfigurationService, IDisposableRegistry } from '../types';
1515
import {
@@ -24,6 +24,11 @@ import { sleep } from '../utils/async';
2424
import { useEnvExtension } from '../../envExt/api.internal';
2525
import { ensureTerminalLegacy } from '../../envExt/api.legacy';
2626

27+
interface QueuedCommand {
28+
commandLine: string;
29+
resolve: (value: TerminalShellExecution | undefined) => void;
30+
}
31+
2732
@injectable()
2833
export class TerminalService implements ITerminalService, Disposable {
2934
private terminal?: Terminal;
@@ -33,8 +38,12 @@ export class TerminalService implements ITerminalService, Disposable {
3338
private terminalHelper: ITerminalHelper;
3439
private terminalActivator: ITerminalActivator;
3540
private terminalAutoActivator: ITerminalAutoActivation;
41+
private applicationShell: IApplicationShell;
3642
private readonly executeCommandListeners: Set<Disposable> = new Set();
3743
private _terminalFirstLaunched: boolean = true;
44+
private pythonReplCommandQueue: QueuedCommand[] = [];
45+
private isReplReady: boolean = false;
46+
private replDataListener?: Disposable;
3847
public get onDidCloseTerminal(): Event<void> {
3948
return this.terminalClosed.event.bind(this.terminalClosed);
4049
}
@@ -48,11 +57,13 @@ export class TerminalService implements ITerminalService, Disposable {
4857
this.terminalHelper = this.serviceContainer.get<ITerminalHelper>(ITerminalHelper);
4958
this.terminalManager = this.serviceContainer.get<ITerminalManager>(ITerminalManager);
5059
this.terminalAutoActivator = this.serviceContainer.get<ITerminalAutoActivation>(ITerminalAutoActivation);
60+
this.applicationShell = this.serviceContainer.get<IApplicationShell>(IApplicationShell);
5161
this.terminalManager.onDidCloseTerminal(this.terminalCloseHandler, this, disposableRegistry);
5262
this.terminalActivator = this.serviceContainer.get<ITerminalActivator>(ITerminalActivator);
5363
}
5464
public dispose() {
5565
this.terminal?.dispose();
66+
this.disposeReplListener();
5667

5768
if (this.executeCommandListeners && this.executeCommandListeners.size > 0) {
5869
this.executeCommandListeners.forEach((d) => {
@@ -81,14 +92,76 @@ export class TerminalService implements ITerminalService, Disposable {
8192
commandLine: string,
8293
isPythonShell: boolean,
8394
): Promise<TerminalShellExecution | undefined> {
84-
// TODO: First execution of shift+enter may get ignored when using sendText.
85-
// Prevent Cannot read properties of undefined: https://github.com/microsoft/vscode-python-environments/issues/958
86-
if (!this.terminal) {
87-
traceVerbose('Terminal not available yet, cannot execute command');
88-
return undefined;
95+
if (isPythonShell) {
96+
if (this.isReplReady) {
97+
return this.executeCommandInternal(commandLine, true);
98+
}
99+
100+
// Queue command and start listening for REPL prompt if not already
101+
return new Promise<TerminalShellExecution | undefined>((resolve) => {
102+
this.pythonReplCommandQueue.push({ commandLine, resolve });
103+
traceVerbose(`Queued Python REPL command: ${commandLine}`);
104+
this.startReplListener();
105+
});
106+
}
107+
108+
// For non-Python shell commands, execute directly
109+
return this.executeCommandInternal(commandLine, isPythonShell);
110+
}
111+
112+
/**
113+
* Starts listening for the Python REPL prompt (>>>).
114+
* When detected, processes all queued commands.
115+
*/
116+
private startReplListener(): void {
117+
if (this.replDataListener) {
118+
return;
89119
}
90120

121+
let terminalData = '';
122+
123+
this.replDataListener = this.applicationShell.onDidWriteTerminalData((e) => {
124+
if (this.terminal && e.terminal === this.terminal) {
125+
terminalData += e.data;
126+
// Check for Python REPL prompt (>>>)
127+
if (/>>>\s*$/.test(terminalData)) {
128+
traceVerbose('Python REPL ready, detected >>> prompt');
129+
this.isReplReady = true;
130+
this.disposeReplListener();
131+
this.flushReplQueue();
132+
}
133+
}
134+
});
135+
}
136+
137+
private disposeReplListener(): void {
138+
if (this.replDataListener) {
139+
this.replDataListener.dispose();
140+
this.replDataListener = undefined;
141+
}
142+
}
143+
144+
private async flushReplQueue(): Promise<void> {
145+
while (this.pythonReplCommandQueue.length > 0) {
146+
const cmd = this.pythonReplCommandQueue.shift();
147+
if (cmd) {
148+
traceVerbose(`Executing queued REPL command: ${cmd.commandLine}`);
149+
const result = await this.executeCommandInternal(cmd.commandLine, true);
150+
cmd.resolve(result);
151+
}
152+
}
153+
}
154+
155+
private async executeCommandInternal(
156+
commandLine: string,
157+
isPythonShell: boolean,
158+
): Promise<TerminalShellExecution | undefined> {
91159
const terminal = this.terminal;
160+
if (!terminal) {
161+
traceVerbose('Terminal not available, cannot execute command');
162+
return undefined;
163+
}
164+
92165
if (!this.options?.hideFromUser) {
93166
terminal.show(true);
94167
}
@@ -175,6 +248,14 @@ export class TerminalService implements ITerminalService, Disposable {
175248
if (terminal === this.terminal) {
176249
this.terminalClosed.fire();
177250
this.terminal = undefined;
251+
// Reset REPL state when terminal closes
252+
this.isReplReady = false;
253+
this.disposeReplListener();
254+
// Clear any pending commands
255+
while (this.pythonReplCommandQueue.length > 0) {
256+
const cmd = this.pythonReplCommandQueue.shift();
257+
cmd?.resolve(undefined);
258+
}
178259
}
179260
}
180261

0 commit comments

Comments
 (0)