Skip to content

Commit cecefa5

Browse files
Copilotanthonykim1
andcommitted
Implement REPL detection for file execution and add test
Co-authored-by: anthonykim1 <[email protected]>
1 parent 1c5c9b2 commit cecefa5

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

src/client/terminals/codeExecution/terminalCodeExecution.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService {
3838
) {}
3939

4040
public async executeFile(file: Uri, options?: { newTerminalPerFile: boolean }) {
41-
await this.setCwdForFileExecution(file, options);
41+
// If we're currently in a REPL, force creation of a new terminal for file execution
42+
const finalOptions = await this.shouldForceNewTerminalForFileExecution(options);
43+
44+
await this.setCwdForFileExecution(file, finalOptions);
4245
const { command, args } = await this.getExecuteFileArgs(file, [
4346
file.fsPath.fileToCommandArgumentForPythonExt(),
4447
]);
4548

46-
await this.getTerminalService(file, options).sendCommand(command, args);
49+
await this.getTerminalService(file, finalOptions).sendCommand(command, args);
4750
}
4851

4952
public async execute(code: string, resource?: Uri): Promise<void> {
@@ -124,6 +127,28 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService {
124127
public async getExecuteFileArgs(resource?: Uri, executeArgs: string[] = []): Promise<PythonExecInfo> {
125128
return this.getExecutableInfo(resource, executeArgs);
126129
}
130+
131+
/**
132+
* Check if we should force creation of a new terminal for file execution.
133+
* This is needed when the current terminal is in Python REPL mode to avoid
134+
* sending the file execution command to the Python interpreter instead of the shell.
135+
*/
136+
private async shouldForceNewTerminalForFileExecution(
137+
options?: { newTerminalPerFile: boolean }
138+
): Promise<{ newTerminalPerFile: boolean }> {
139+
// If already set to create new terminal, respect that
140+
if (options?.newTerminalPerFile) {
141+
return { newTerminalPerFile: true };
142+
}
143+
144+
// If we have an active REPL, force creation of a new terminal
145+
if (this.replActive && (await this.replActive)) {
146+
return { newTerminalPerFile: true };
147+
}
148+
149+
// Otherwise, use original options or default to false
150+
return options || { newTerminalPerFile: false };
151+
}
127152
private getTerminalService(resource: Resource, options?: { newTerminalPerFile: boolean }): ITerminalService {
128153
return this.terminalServiceFactory.getTerminalService({
129154
resource,

src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,43 @@ suite('Terminal - Code Execution', () => {
649649
terminalService.verify(async (t) => t.executeCommand('cmd2', true), TypeMoq.Times.once());
650650
});
651651

652+
test('Ensure new terminal is created when REPL is active and executeFile is called', async () => {
653+
// Setup a file to execute
654+
const file = Uri.file(path.join('c:', 'path', 'to', 'file', 'test.py'));
655+
656+
// Setup mocks for interpreter and settings
657+
const pythonPath = 'usr/bin/python1234';
658+
const terminalArgs = ['-a', 'b', 'c'];
659+
platform.setup((p) => p.isWindows).returns(() => false);
660+
interpreterService
661+
.setup((s) => s.getActiveInterpreter(TypeMoq.It.isAny()))
662+
.returns(() => Promise.resolve(({ path: pythonPath } as unknown) as PythonEnvironment));
663+
terminalSettings.setup((t) => t.launchArgs).returns(() => terminalArgs);
664+
terminalSettings.setup((t) => t.executeInFileDir).returns(() => false);
665+
666+
// First, initialize REPL to make replActive = true
667+
await executor.initializeRepl(file);
668+
669+
// Reset terminal factory to track calls for file execution
670+
terminalFactory.reset();
671+
terminalFactory
672+
.setup((f) => f.getTerminalService(TypeMoq.It.isAny()))
673+
.callback((options: TerminalCreationOptions & { newTerminalPerFile?: boolean }) => {
674+
// When REPL is active, executeFile should force newTerminalPerFile = true
675+
assert.equal(options.newTerminalPerFile, true, 'Should force new terminal when REPL is active');
676+
})
677+
.returns(() => terminalService.object);
678+
679+
// Execute file - this should force creation of a new terminal
680+
await executor.executeFile(file);
681+
682+
// Verify that getTerminalService was called with newTerminalPerFile: true
683+
terminalFactory.verify(
684+
(f) => f.getTerminalService(TypeMoq.It.isAny()),
685+
TypeMoq.Times.once()
686+
);
687+
});
688+
652689
test('Ensure code is sent to the same terminal for a particular resource', async () => {
653690
const resource = Uri.file('a');
654691
terminalFactory.reset();

0 commit comments

Comments
 (0)