Skip to content

Commit bf2cba8

Browse files
committed
Ensure sent code is run
1 parent e6843db commit bf2cba8

2 files changed

Lines changed: 33 additions & 2 deletions

File tree

‎src/client/common/terminal/service.ts‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export class TerminalService implements ITerminalService, Disposable {
9090
): Promise<TerminalShellExecution | undefined> {
9191
if (isPythonShell) {
9292
if (this.isReplReady) {
93-
this.terminal?.sendText(commandLine);
93+
this.sendPythonShellCommand(commandLine);
9494
traceVerbose(`Python REPL sendText: ${commandLine}`);
9595
} else {
9696
// Queue command to run once REPL is ready.
@@ -156,11 +156,21 @@ export class TerminalService implements ITerminalService, Disposable {
156156
const commandLine = this.pythonReplCommandQueue.shift();
157157
if (commandLine) {
158158
traceVerbose(`Executing queued REPL command: ${commandLine}`);
159-
this.terminal?.sendText(commandLine);
159+
this.sendPythonShellCommand(commandLine);
160160
}
161161
}
162162
}
163163

164+
// PyREPL requires Enter to arrive after the bracketed-paste sequence has been fully processed.
165+
private sendPythonShellCommand(commandLine: string): void {
166+
if (commandLine.startsWith('\u001b[200~') && commandLine.endsWith('\u001b[201~')) {
167+
this.terminal?.sendText(commandLine, false);
168+
this.terminal?.sendText('', true);
169+
} else {
170+
this.terminal?.sendText(commandLine);
171+
}
172+
}
173+
164174
private async executeCommandInternal(commandLine: string): Promise<TerminalShellExecution | undefined> {
165175
const terminal = this.terminal;
166176
if (!terminal) {

‎src/test/common/terminals/service.unit.test.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,27 @@ suite('Terminal Service', () => {
338338
terminal.verify((t) => t.sendText(TypeMoq.It.isValue(textToSend)), TypeMoq.Times.once());
339339
});
340340

341+
test('Ensure bracketed paste and Enter are sent separately to the Python shell', async () => {
342+
terminalHelper
343+
.setup((helper) => helper.getEnvironmentActivationCommands(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
344+
.returns(() => Promise.resolve(undefined));
345+
service = new TerminalService(mockServiceContainer.object);
346+
const textToSend = '\u001b[200~print("hello")\u001b[201~';
347+
terminalHelper.setup((h) => h.identifyTerminalShell(TypeMoq.It.isAny())).returns(() => TerminalShellType.bash);
348+
terminalManager.setup((t) => t.createTerminal(TypeMoq.It.isAny())).returns(() => terminal.object);
349+
350+
await service.ensureTerminal();
351+
const executePromise = service.executeCommand(textToSend, true);
352+
onDidWriteTerminalDataEmitter.fire({ terminal: terminal.object, data: '>>> ' });
353+
await executePromise;
354+
355+
terminal.verify(
356+
(t) => t.sendText(TypeMoq.It.isValue(textToSend), TypeMoq.It.isValue(false)),
357+
TypeMoq.Times.once(),
358+
);
359+
terminal.verify((t) => t.sendText(TypeMoq.It.isValue(''), TypeMoq.It.isValue(true)), TypeMoq.Times.once());
360+
});
361+
341362
test('Ensure sendText IS called even when Python shell integration and terminal shell integration are both enabled - Window', async () => {
342363
isWindowsStub.returns(true);
343364
pythonConfig

0 commit comments

Comments
 (0)