Skip to content

Commit c7a1504

Browse files
committed
Start on partial keyboard interrupt fixes
Co-authored-by: Daniel Imms <[email protected]>
1 parent b1cb5c2 commit c7a1504

File tree

5 files changed

+50
-15
lines changed

5 files changed

+50
-15
lines changed

src/client/common/terminal/service.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22
// Licensed under the MIT License.
33

44
import { inject, injectable } from 'inversify';
5-
import { CancellationToken, Disposable, Event, EventEmitter, Terminal, TerminalShellExecution } from 'vscode';
5+
import { CancellationToken, Disposable, Event, EventEmitter, Terminal, window } from 'vscode';
66
import '../../common/extensions';
77
import { IInterpreterService } from '../../interpreter/contracts';
88
import { IServiceContainer } from '../../ioc/types';
99
import { captureTelemetry } from '../../telemetry';
1010
import { EventName } from '../../telemetry/constants';
11-
import { ITerminalAutoActivation } from '../../terminals/types';
11+
import { ICodeExecutionService, ITerminalAutoActivation } from '../../terminals/types';
1212
import { ITerminalManager } from '../application/types';
1313
import { _SCRIPTS_DIR } from '../process/internal/scripts/constants';
1414
import { IConfigurationService, IDisposableRegistry } from '../types';
1515
import {
16+
IExecuteCommandResult,
1617
ITerminalActivator,
1718
ITerminalHelper,
1819
ITerminalService,
@@ -57,14 +58,18 @@ export class TerminalService implements ITerminalService, Disposable {
5758
});
5859
}
5960
}
60-
public async sendCommand(command: string, args: string[], _?: CancellationToken): Promise<void> {
61+
public async sendCommand(
62+
command: string,
63+
args: string[],
64+
_?: CancellationToken,
65+
): Promise<IExecuteCommandResult | undefined> {
6166
await this.ensureTerminal();
6267
const text = this.terminalHelper.buildCommandForTerminal(this.terminalShellType, command, args);
6368
if (!this.options?.hideFromUser) {
6469
this.terminal!.show(true);
6570
}
6671

67-
await this.executeCommand(text);
72+
return this.executeCommand(text);
6873
}
6974
/** @deprecated */
7075
public async sendText(text: string): Promise<void> {
@@ -74,7 +79,7 @@ export class TerminalService implements ITerminalService, Disposable {
7479
}
7580
this.terminal!.sendText(text);
7681
}
77-
public async executeCommand(commandLine: string): Promise<TerminalShellExecution | undefined> {
82+
public async executeCommand(commandLine: string): Promise<IExecuteCommandResult | undefined> {
7883
const terminal = this.terminal!;
7984
if (!this.options?.hideFromUser) {
8085
terminal.show(true);
@@ -97,11 +102,26 @@ export class TerminalService implements ITerminalService, Disposable {
97102
});
98103
await promise;
99104
}
100-
105+
// python in a shell , exit code is undefined . startCommand event happen, we call end command event
101106
if (terminal.shellIntegration) {
107+
// TODO: Await the python REPL execute promise here. So we know python repl launched for sure before executing other python code.
108+
// So we would not be interrupted.
109+
110+
await this.serviceContainer.get<ICodeExecutionService>(ICodeExecutionService).replActive;
111+
102112
const execution = terminal.shellIntegration.executeCommand(commandLine);
103113
traceVerbose(`Shell Integration is enabled, executeCommand: ${commandLine}`);
104-
return execution;
114+
return {
115+
execution,
116+
exitCode: new Promise((resolve) => {
117+
const disposable = window.onDidEndTerminalShellExecution((event) => {
118+
if (event.execution === execution) {
119+
disposable.dispose();
120+
resolve(event.exitCode);
121+
}
122+
});
123+
}),
124+
};
105125
} else {
106126
terminal.sendText(commandLine);
107127
traceVerbose(`Shell Integration is disabled, sendText: ${commandLine}`);

src/client/common/terminal/syncTerminalService.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
'use strict';
55

66
import { inject } from 'inversify';
7-
import { CancellationToken, Disposable, Event, TerminalShellExecution } from 'vscode';
7+
import { CancellationToken, Disposable, Event } from 'vscode';
88
import { IInterpreterService } from '../../interpreter/contracts';
99
import { traceVerbose } from '../../logging';
1010
import { PythonEnvironment } from '../../pythonEnvironments/info';
@@ -14,7 +14,7 @@ import * as internalScripts from '../process/internal/scripts';
1414
import { createDeferred, Deferred } from '../utils/async';
1515
import { noop } from '../utils/misc';
1616
import { TerminalService } from './service';
17-
import { ITerminalService } from './types';
17+
import { IExecuteCommandResult, ITerminalService } from './types';
1818

1919
enum State {
2020
notStarted = 0,
@@ -124,7 +124,7 @@ export class SynchronousTerminalService implements ITerminalService, Disposable
124124
args: string[],
125125
cancel?: CancellationToken,
126126
swallowExceptions: boolean = true,
127-
): Promise<void> {
127+
): Promise<IExecuteCommandResult | undefined> {
128128
if (!cancel) {
129129
return this.terminalService.sendCommand(command, args);
130130
}
@@ -145,7 +145,7 @@ export class SynchronousTerminalService implements ITerminalService, Disposable
145145
public sendText(text: string): Promise<void> {
146146
return this.terminalService.sendText(text);
147147
}
148-
public executeCommand(commandLine: string): Promise<TerminalShellExecution | undefined> {
148+
public async executeCommand(commandLine: string): Promise<IExecuteCommandResult | undefined> {
149149
return this.terminalService.executeCommand(commandLine);
150150
}
151151
public show(preserveFocus?: boolean | undefined): Promise<void> {

src/client/common/terminal/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,20 @@ export interface ITerminalService extends IDisposable {
5151
args: string[],
5252
cancel?: CancellationToken,
5353
swallowExceptions?: boolean,
54-
): Promise<void>;
54+
): Promise<IExecuteCommandResult | undefined>;
5555
/** @deprecated */
5656
sendText(text: string): Promise<void>;
57-
executeCommand(commandLine: string): Promise<TerminalShellExecution | undefined>;
57+
executeCommand(commandLine: string): Promise<IExecuteCommandResult | undefined>;
5858
show(preserveFocus?: boolean): Promise<void>;
5959
}
6060

6161
export const ITerminalServiceFactory = Symbol('ITerminalServiceFactory');
6262

63+
export interface IExecuteCommandResult {
64+
execution: TerminalShellExecution;
65+
exitCode: Promise<number | undefined>;
66+
}
67+
6368
export type TerminalCreationOptions = {
6469
/**
6570
* Object with environment variables that will be added to the Terminal.

src/client/terminals/codeExecution/terminalCodeExecution.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { sendTelemetryEvent } from '../../telemetry';
2424
export class TerminalCodeExecutionProvider implements ICodeExecutionService {
2525
private hasRanOutsideCurrentDrive = false;
2626
protected terminalTitle!: string;
27-
private replActive?: Promise<boolean>;
27+
replActive?: Promise<boolean>;
2828

2929
constructor(
3030
@inject(ITerminalServiceFactory) protected readonly terminalServiceFactory: ITerminalServiceFactory,
@@ -73,6 +73,7 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService {
7373
this.replActive = new Promise<boolean>(async (resolve) => {
7474
const replCommandArgs = await this.getExecutableInfo(resource);
7575
let listener: IDisposable;
76+
// TODO: There's a race condition here as well; the send text from file command may happen before this timeout resolves
7677
Promise.race([
7778
new Promise<boolean>((resolve) => setTimeout(() => resolve(true), 3000)),
7879
new Promise<boolean>((resolve) => {
@@ -99,9 +100,17 @@ export class TerminalCodeExecutionProvider implements ICodeExecutionService {
99100
}
100101
resolve(true);
101102
});
103+
// python
104+
// python shell, exit code undefined.
105+
// ondidStartShellExecution happens, si fires ondidEndsi
102106

103-
await terminalService.sendCommand(replCommandArgs.command, replCommandArgs.args);
107+
// TODO: Store this promise and make sure no further commands are executed without awaiting .exitCode
108+
const replExecution = await terminalService.sendCommand(replCommandArgs.command, replCommandArgs.args); // need to make sure this is resolved before starting executing something in repl.
109+
110+
// TODO: Should we await replyExecution.exitCode here?
111+
await replExecution?.exitCode;
104112
});
113+
105114
this.disposables.push(
106115
terminalService.onDidCloseTerminal(() => {
107116
this.replActive = undefined;

src/client/terminals/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { IDisposable, Resource } from '../common/types';
77
export const ICodeExecutionService = Symbol('ICodeExecutionService');
88

99
export interface ICodeExecutionService {
10+
replActive?: Promise<boolean>;
1011
execute(code: string, resource?: Uri): Promise<void>;
1112
executeFile(file: Uri, options?: { newTerminalPerFile: boolean }): Promise<void>;
1213
initializeRepl(resource?: Uri): Promise<void>;

0 commit comments

Comments
 (0)